Related
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.
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:
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++.
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.
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).
I found this on another forum while looking to solve the same problem. I've modified it a bit from what I found. It works great. I'm running OS X, so if you're running Microsoft, you'll need to find the correct system() command to switch to raw and cooked modes.
#include <iostream>
#include <stdio.h>
using namespace std;
int main() {
// Output prompt
cout << "Press any key to continue..." << endl;
// Set terminal to raw mode
system("stty raw");
// Wait for single character
char input = getchar();
// Echo input:
cout << "--" << input << "--";
// Reset terminal to normal "cooked" mode
system("stty cooked");
// And we're out of here
return 0;
}
If you are on windows, you can use PeekConsoleInput to detect if there's any input,
HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
DWORD events;
INPUT_RECORD buffer;
PeekConsoleInput( handle, &buffer, 1, &events );
then use ReadConsoleInput to "consume" the input character ..
PeekConsoleInput(handle, &buffer, 1, &events);
if(events > 0)
{
ReadConsoleInput(handle, &buffer, 1, &events);
return buffer.Event.KeyEvent.wVirtualKeyCode;
}
else return 0
to be honest this is from some old code I have, so you have to fiddle a bit with it.
The cool thing though is that it reads input without prompting for anything, so the characters are not displayed at all.
CONIO.H
the functions you need are:
int getch();
Prototype
int _getch(void);
Description
_getch obtains a character from stdin. Input is unbuffered, and this
routine will return as soon as a character is available without
waiting for a carriage return. The character is not echoed to stdout.
_getch bypasses the normal buffering done by getchar and getc. ungetc
cannot be used with _getch.
Synonym
Function: getch
int kbhit();
Description
Checks if a keyboard key has been pressed but not yet read.
Return Value
Returns a non-zero value if a key was pressed. Otherwise, returns 0.
libconio
http://sourceforge.net/projects/libconio
or
Linux c++ implementation of conio.h
http://sourceforge.net/projects/linux-conioh
#include <conio.h>
if (kbhit() != 0) {
cout << getch() << endl;
}
This uses kbhit() to check if the keyboard is being pressed and uses getch() to get the character that is being pressed.
I use kbhit() to see if a char is present and then getchar() to read the data.
On windows, you can use "conio.h". On linux, you will have to implement your own kbhit().
See code below:
// kbhit
#include <stdio.h>
#include <sys/ioctl.h> // For FIONREAD
#include <termios.h>
#include <stdbool.h>
int kbhit(void) {
static bool initflag = false;
static const int STDIN = 0;
if (!initflag) {
// Use termios to turn off line buffering
struct termios term;
tcgetattr(STDIN, &term);
term.c_lflag &= ~ICANON;
tcsetattr(STDIN, TCSANOW, &term);
setbuf(stdin, NULL);
initflag = true;
}
int nbbytes;
ioctl(STDIN, FIONREAD, &nbbytes); // 0 is STDIN
return nbbytes;
}
// main
#include <unistd.h>
int main(int argc, char** argv) {
char c;
//setbuf(stdout, NULL); // Optional: No buffering.
//setbuf(stdin, NULL); // Optional: No buffering.
printf("Press key");
while (!kbhit()) {
printf(".");
fflush(stdout);
sleep(1);
}
c = getchar();
printf("\nChar received:%c\n", c);
printf("Done.\n");
return 0;
}
ncurses provides a nice way to do this!
Also this is my very first post (that I can remember), so any comments at all are welcome. I will appreciate useful ones, but all are welcome!
to compile: g++ -std=c++11 -pthread -lncurses .cpp -o
#include <iostream>
#include <ncurses.h>
#include <future>
char get_keyboard_input();
int main(int argc, char *argv[])
{
initscr();
raw();
noecho();
keypad(stdscr,true);
auto f = std::async(std::launch::async, get_keyboard_input);
while (f.wait_for(std::chrono::milliseconds(20)) != std::future_status::ready)
{
// do some work
}
endwin();
std::cout << "returned: " << f.get() << std::endl;
return 0;
}
char get_keyboard_input()
{
char input = '0';
while(input != 'q')
{
input = getch();
}
return input;
}
Since previous solutions here don't work cross-platform and have trouble with special keys, here is my solution that works on both Windows and Linux and uses a minimum of external libraries (Windows.h for Windows and sys/ioctl.h+termios.h for Linux).
For ASCII characters (newline/tab/space/backspace/delete, !"#$%&'()*+,-./0-9:;<=>?#A-Z[]^_`a-z{|}~üäÄöÖÜßµ´§°¹³²), the ASCII codes (positive numbers) are returned and for special keys (arrow keys, page up/down, pos1/end, escape, insert, F1-F12), the negative of Windows Virtual-Key codes (negative numbers) are returned.
#include <iostream>
#include <string>
#include <thread> // contains <chrono>
using namespace std;
void println(const string& s="") {
cout << s << endl;
}
void sleep(const double t) {
if(t>0.0) this_thread::sleep_for(chrono::milliseconds((int)(1E3*t+0.5)));
}
// ASCII codes (key>0): 8 backspace, 9 tab, 10 newline, 27 escape, 127 delete, !"#$%&'()*+,-./0-9:;<=>?#A-Z[]^_`a-z{|}~üäÄöÖÜßµ´§°¹³²
// control key codes (key<0): -38/-40/-37/-39 up/down/left/right arrow, -33/-34 page up/down, -36/-35 pos1/end
// other key codes (key<0): -45 insert, -144 num lock, -20 caps lock, -91 windows key, -93 kontext menu key, -112 to -123 F1 to F12
// not working: ¹ (251), num lock (-144), caps lock (-20), windows key (-91), kontext menu key (-93), F11 (-122)
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#include <Windows.h>
int key_press() { // not working: F11 (-122, toggles fullscreen)
KEY_EVENT_RECORD keyevent;
INPUT_RECORD irec;
DWORD events;
while(true) {
ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &irec, 1, &events);
if(irec.EventType==KEY_EVENT&&((KEY_EVENT_RECORD&)irec.Event).bKeyDown) {
keyevent = (KEY_EVENT_RECORD&)irec.Event;
const int ca = (int)keyevent.uChar.AsciiChar;
const int cv = (int)keyevent.wVirtualKeyCode;
const int key = ca==0 ? -cv : ca+(ca>0?0:256);
switch(key) {
case -16: continue; // disable Shift
case -17: continue; // disable Ctrl / AltGr
case -18: continue; // disable Alt / AltGr
case -220: continue; // disable first detection of "^" key (not "^" symbol)
case -221: continue; // disable first detection of "`" key (not "`" symbol)
case -191: continue; // disable AltGr + "#"
case -52: continue; // disable AltGr + "4"
case -53: continue; // disable AltGr + "5"
case -54: continue; // disable AltGr + "6"
case -12: continue; // disable num block 5 with num lock deactivated
case 13: return 10; // enter
case -46: return 127; // delete
case -49: return 251; // ¹
case 0: continue;
case 1: continue; // disable Ctrl + a (selects all text)
case 2: continue; // disable Ctrl + b
case 3: continue; // disable Ctrl + c (terminates program)
case 4: continue; // disable Ctrl + d
case 5: continue; // disable Ctrl + e
case 6: continue; // disable Ctrl + f (opens search)
case 7: continue; // disable Ctrl + g
//case 8: continue; // disable Ctrl + h (ascii for backspace)
//case 9: continue; // disable Ctrl + i (ascii for tab)
case 10: continue; // disable Ctrl + j
case 11: continue; // disable Ctrl + k
case 12: continue; // disable Ctrl + l
//case 13: continue; // disable Ctrl + m (breaks console, ascii for new line)
case 14: continue; // disable Ctrl + n
case 15: continue; // disable Ctrl + o
case 16: continue; // disable Ctrl + p
case 17: continue; // disable Ctrl + q
case 18: continue; // disable Ctrl + r
case 19: continue; // disable Ctrl + s
case 20: continue; // disable Ctrl + t
case 21: continue; // disable Ctrl + u
case 22: continue; // disable Ctrl + v (inserts clipboard)
case 23: continue; // disable Ctrl + w
case 24: continue; // disable Ctrl + x
case 25: continue; // disable Ctrl + y
case 26: continue; // disable Ctrl + z
default: return key; // any other ASCII/virtual character
}
}
}
}
#elif defined(__linux__)
#include <sys/ioctl.h>
#include <termios.h>
int key_press() { // not working: ¹ (251), num lock (-144), caps lock (-20), windows key (-91), kontext menu key (-93)
struct termios term;
tcgetattr(0, &term);
while(true) {
term.c_lflag &= ~(ICANON|ECHO); // turn off line buffering and echoing
tcsetattr(0, TCSANOW, &term);
int nbbytes;
ioctl(0, FIONREAD, &nbbytes); // 0 is STDIN
while(!nbbytes) {
sleep(0.01);
fflush(stdout);
ioctl(0, FIONREAD, &nbbytes); // 0 is STDIN
}
int key = (int)getchar();
if(key==27||key==194||key==195) { // escape, 194/195 is escape for °ß´äöüÄÖÜ
key = (int)getchar();
if(key==91) { // [ following escape
key = (int)getchar(); // get code of next char after \e[
if(key==49) { // F5-F8
key = 62+(int)getchar(); // 53, 55-57
if(key==115) key++; // F5 code is too low by 1
getchar(); // take in following ~ (126), but discard code
} else if(key==50) { // insert or F9-F12
key = (int)getchar();
if(key==126) { // insert
key = 45;
} else { // F9-F12
key += 71; // 48, 49, 51, 52
if(key<121) key++; // F11 and F12 are too low by 1
getchar(); // take in following ~ (126), but discard code
}
} else if(key==51||key==53||key==54) { // delete, page up/down
getchar(); // take in following ~ (126), but discard code
}
} else if(key==79) { // F1-F4
key = 32+(int)getchar(); // 80-83
}
key = -key; // use negative numbers for escaped keys
}
term.c_lflag |= (ICANON|ECHO); // turn on line buffering and echoing
tcsetattr(0, TCSANOW, &term);
switch(key) {
case 127: return 8; // backspace
case -27: return 27; // escape
case -51: return 127; // delete
case -164: return 132; // ä
case -182: return 148; // ö
case -188: return 129; // ü
case -132: return 142; // Ä
case -150: return 153; // Ö
case -156: return 154; // Ü
case -159: return 225; // ß
case -181: return 230; // µ
case -167: return 245; // §
case -176: return 248; // °
case -178: return 253; // ²
case -179: return 252; // ³
case -180: return 239; // ´
case -65: return -38; // up arrow
case -66: return -40; // down arrow
case -68: return -37; // left arrow
case -67: return -39; // right arrow
case -53: return -33; // page up
case -54: return -34; // page down
case -72: return -36; // pos1
case -70: return -35; // end
case 0: continue;
case 1: continue; // disable Ctrl + a
case 2: continue; // disable Ctrl + b
case 3: continue; // disable Ctrl + c (terminates program)
case 4: continue; // disable Ctrl + d
case 5: continue; // disable Ctrl + e
case 6: continue; // disable Ctrl + f
case 7: continue; // disable Ctrl + g
case 8: continue; // disable Ctrl + h
//case 9: continue; // disable Ctrl + i (ascii for tab)
//case 10: continue; // disable Ctrl + j (ascii for new line)
case 11: continue; // disable Ctrl + k
case 12: continue; // disable Ctrl + l
case 13: continue; // disable Ctrl + m
case 14: continue; // disable Ctrl + n
case 15: continue; // disable Ctrl + o
case 16: continue; // disable Ctrl + p
case 17: continue; // disable Ctrl + q
case 18: continue; // disable Ctrl + r
case 19: continue; // disable Ctrl + s
case 20: continue; // disable Ctrl + t
case 21: continue; // disable Ctrl + u
case 22: continue; // disable Ctrl + v
case 23: continue; // disable Ctrl + w
case 24: continue; // disable Ctrl + x
case 25: continue; // disable Ctrl + y
case 26: continue; // disable Ctrl + z (terminates program)
default: return key; // any other ASCII character
}
}
}
#endif // Windows/Linux
Finally, here is an example on how to use it:
int main() {
while(true) {
const int key = key_press(); // blocks until a key is pressed
println("Input is: "+to_string(key)+", \""+(char)key+"\"");
}
return 0;
}
Assuming Windows, take a look at the ReadConsoleInput function.
C and C++ take a very abstract view of I/O, and there is no standard way of doing what you want. There are standard ways to get characters from the standard input stream, if there are any to get, and nothing else is defined by either language. Any answer will therefore have to be platform-specific, perhaps depending not only on the operating system but also the software framework.
There's some reasonable guesses here, but there's no way to answer your question without knowing what your target environment is.
The closest thing to portable is to use the ncurses library to put the terminal into "cbreak mode". The API is gigantic; the routines you'll want most are
initscr and endwin
cbreak and nocbreak
getch
Good luck!
I always wanted a loop to read my input without pressing return key.
this worked for me.
#include<stdio.h>
main()
{
char ch;
system("stty raw");//seting the terminal in raw mode
while(1)
{
ch=getchar();
if(ch=='~'){ //terminate or come out of raw mode on "~" pressed
system("stty cooked");
//while(1);//you may still run the code
exit(0); //or terminate
}
printf("you pressed %c\n ",ch); //write rest code here
}
}
works for me on windows:
#include <conio.h>
char c = _getch();
The following is a solution extracted from Expert C Programming: Deep Secrets, which is supposed to work on SVr4. It uses stty and ioctl.
#include <sys/filio.h>
int kbhit()
{
int i;
ioctl(0, FIONREAD, &i);
return i; /* return a count of chars available to read */
}
main()
{
int i = 0;
intc='';
system("stty raw -echo");
printf("enter 'q' to quit \n");
for (;c!='q';i++) {
if (kbhit()) {
c=getchar();
printf("\n got %c, on iteration %d",c, i);
}
}
system("stty cooked echo");
}
You can do it portably using SDL (the Simple DirectMedia Library), though I suspect you may not like its behavior. When I tried it, I had to have SDL create a new video window (even though I didn't need it for my program) and have this window "grab" almost all keyboard and mouse input (which was okay for my usage but could be annoying or unworkable in other situations). I suspect it's overkill and not worth it unless complete portability is a must--otherwise try one of the other suggested solutions.
By the way, this will give you key press and release events separately, if you're into that.
Variant of ssinfod's answer for Linux that's a little cleaner for my tastes, implemented for wcout and wchar_t, and erases invalid characters without bugs.
#include <functional>
//For Linux kbhit(). For Windows, use conio.h.
#ifdef __unix__
#include <sys/ioctl.h> //For FIONREAD.
#include <termios.h>
//Call this at program start to setup for kbhit.
void initTerminalInput()
{
//Disable internal buffering.
std::wcout << std::unitbuf;
//Turn off line buffering.
struct termios term;
tcgetattr(0, &term);
term.c_lflag &= ~ICANON;
tcsetattr(0, TCSANOW, &term);
setbuf(stdin, NULL);
}
//Returns 0 if there's no input character to read.
int kbhit()
{
static int nbbytes;
ioctl(0, FIONREAD, &nbbytes);
return nbbytes;
}
#endif
//Waits for and retrieves a single validated character, calling a validation function on each character entered and
//erasing any that are invalid (when the validation function returns false).
static wchar_t getWChar(std::function<bool(wchar_t)> validationFunction)
{
static wchar_t inputWChar;
do
{
//Wait until there's an input character.
while (!kbhit())
{
}
inputWChar = getwchar();
//Validate the input character.
if (validationFunction(inputWChar))
{
//Valid.
break;
}
else
{
//Erase the invalid character.
std::wcout << L"\b \b";
}
} while (true);
return inputWChar;
}
In the below example, I wanted for the user to enter either 1, 2, or 3. Any other characters entered will not be displayed, and it will wait until one of the valid characters is pressed:
int main()
{
#ifdef __unix__
initTerminalInput();
#endif
getWChar([] (wchar_t inputWChar)
{
return (inputWChar >= L'1' && inputWChar <= L'3');
});
return 0;
}
Here's a version that doesn't shell out to the system (written and tested on macOS 10.14)
#include <unistd.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
char* getStr( char* buffer , int maxRead ) {
int numRead = 0;
char ch;
struct termios old = {0};
struct termios new = {0};
if( tcgetattr( 0 , &old ) < 0 ) perror( "tcgetattr() old settings" );
if( tcgetattr( 0 , &new ) < 0 ) perror( "tcgetaart() new settings" );
cfmakeraw( &new );
if( tcsetattr( 0 , TCSADRAIN , &new ) < 0 ) perror( "tcssetattr makeraw new" );
for( int i = 0 ; i < maxRead ; i++) {
ch = getchar();
switch( ch ) {
case EOF:
case '\n':
case '\r':
goto exit_getStr;
break;
default:
printf( "%1c" , ch );
buffer[ numRead++ ] = ch;
if( numRead >= maxRead ) {
goto exit_getStr;
}
break;
}
}
exit_getStr:
if( tcsetattr( 0 , TCSADRAIN , &old) < 0) perror ("tcsetattr reset to old" );
printf( "\n" );
return buffer;
}
int main( void )
{
const int maxChars = 20;
char stringBuffer[ maxChars+1 ];
memset( stringBuffer , 0 , maxChars+1 ); // initialize to 0
printf( "enter a string: ");
getStr( stringBuffer , maxChars );
printf( "you entered: [%s]\n" , stringBuffer );
}
ON WINDOWS I do so:
#include <Windows.h>
int main()
{
system("PAUSE>NUL");
if (GetKeyState(0x41/*(the 'A' key, choosen e.g.)*/) & (0xff80/*That stands for "Default state / Key Down"*/)) {
//whatever I want to do after 'A' is pressed
}
}
Here can be found a list of keys with its own hex value for each:
https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
I was running into this same issue. Here is a small solution for windows console using cygwin g++ with if(GetKeyState(keycode) & bitANDcompare){};.
#include <windows.h>
#include <fstream>
#include <iostream>
using namespace std;
void clear() {
COORD topLeft = { 0, 0 };
HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO screen;
DWORD written;
GetConsoleScreenBufferInfo(console, &screen);
FillConsoleOutputCharacterA(
console, ' ', screen.dwSize.X * screen.dwSize.Y, topLeft, &written
);
FillConsoleOutputAttribute(
console, FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE,
screen.dwSize.X * screen.dwSize.Y, topLeft, &written
);
SetConsoleCursorPosition(console, topLeft);
}
class Keyclick{
private:
int key;
char id;
public:
bool keydown = false;
Keyclick(int key1, char id1){
key=key1;
id=id1;
};
void watch(){
if(keydown==false){
if(GetKeyState(key) & 0x8000 ){
cout << id;
cout << " pressed.\r\n";
keydown = true;
}
}
if(keydown == true){
if(!(GetKeyState(key) & 0x8000)) {
cout << "released!!!!!!!!!!\r\n";
keydown = false;
clear();
}
}
};
};
int main()
{
bool primaryloop =true;
Keyclick keysp(VK_SPACE,'S');
Keyclick keyw(0x57,'w');
Keyclick keya(0x41,'a');
Keyclick keys(0x53,'s');
Keyclick keyd(0x44,'d');
Keyclick keyesc(VK_ESCAPE,'E');
while(primaryloop){
keysp.watch();
keyw.watch();
keya.watch();
keys.watch();
keyd.watch();
keyesc.watch();
if(keyesc.keydown){
primaryloop=false;
};
}
return 0;
}
https://github.com/wark77/windows_console_keypoller/blob/main/getkeystate_SOLUTION01.cpp
Pipe demo: how to read keyboard presses from a system call pipe in C
Also ideally it wouldn't echo the input character to the screen. I just want to capture keystrokes with out effecting the console screen.
To do this on Linux you can use the following bash command:
read -sn1 c && printf "You Pressed: %s\n" "$c"
See my answer here for details on this: shell script respond to keypress.
So, to do this in C or C++ on Linux, you simply need to call the above bash command via a system call through a pipe with popen() and fgets() so you can read the output from the bash command.
Here is a full example which runs fine on Linux in both C and C++:
read_system_call_via_pipe__keypress.c:
#include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
#include <stdint.h> // For `uint8_t`, `int8_t`, etc.
#include <stdio.h> // For `printf()`
#include <stdlib.h>
#define BUFSIZE 32
// Read a keyboard key press and return the character pressed, or a negative
// number in the event of an error.
// NB: for help reading output from system calls, see here:
// 1. https://stackoverflow.com/a/28971647/4561887
// 2. https://stackoverflow.com/a/18297075/4561887
char getKeypress()
{
// This bash cmd is from my answer here:
// https://stackoverflow.com/a/70979348/4561887
const char* cmd = "bash -c 'read -s -n1 c && printf \"%s\" \"$c\"'";
FILE *fp = popen(cmd, "r");
if (fp == NULL)
{
printf("\nError opening pipe!\n");
return -1;
}
char buf[BUFSIZE] = {0};
char* retval1 = fgets(buf, BUFSIZE, fp);
if (retval1 == NULL)
{
printf("\nFailed to read cmd response.\n");
return -2;
}
// See meaning of this return value here:
// https://stackoverflow.com/questions/43116/how-can-i-run-an-external-program-from-c-and-parse-its-output/28971647#comment60311936_28971647
int retval2 = pclose(fp);
if (retval2 == -1)
{
printf("\nError obtaining the cmd's exit status code.\n");
return -3;
}
else if (retval2 != 0)
{
printf("\nCommand exited with exit status code %i.\n", retval2);
return -4;
}
char keyPressed = buf[0];
return keyPressed;
}
// int main(int argc, char *argv[]) // alternative prototype
int main()
{
printf("Press any key to continue: ");
fflush(stdout);
char keyPressed = getKeypress();
if (keyPressed > 0)
{
printf("\nKey pressed = %c\n", keyPressed);
}
return 0;
}
The C and C++ compile and run commands are part of the output below. Here are some demos:
In C:
eRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=gnu17 read_keypress_system_call.c -o bin/a && bin/a
Press any key to continue:
Key pressed = P
OR, in C++:
eRCaGuy_hello_world/c$ g++ -Wall -Wextra -Werror -O3 -std=c++17 read_keypress_system_call.c -o bin/a && bin/a
Press any key to continue:
Key pressed = u
See also:
To take this one step further, see my other answer here where I detect and parse 3 chars at a time in order to detect arrow key presses Up, Down, Left, or Right: Read Key pressings in C ex. Arrow keys, Enter key
References:
How I learned to read from a pipe to get system call output:
How can I run an external program from C and parse its output?
Is there a way to obtain the output of a linux command(like ifconfig) on a .txt file using a C program? [duplicate]
How to compile and use popen() in C: use -std=gnu17 instead of -std=c17: popen implicitly declared even though #include <stdio.h> is added
[my answer] How to read without blocking, via bash: shell script respond to keypress
[my answer] How do I read in the Enter key as an input in C?
All 3 Q&As on this topic
Capture characters from standard input without waiting for enter to be pressed
C non-blocking keyboard input
How to avoid pressing Enter with getchar() for reading a single character only?
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.
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:
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++.
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.
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).
I found this on another forum while looking to solve the same problem. I've modified it a bit from what I found. It works great. I'm running OS X, so if you're running Microsoft, you'll need to find the correct system() command to switch to raw and cooked modes.
#include <iostream>
#include <stdio.h>
using namespace std;
int main() {
// Output prompt
cout << "Press any key to continue..." << endl;
// Set terminal to raw mode
system("stty raw");
// Wait for single character
char input = getchar();
// Echo input:
cout << "--" << input << "--";
// Reset terminal to normal "cooked" mode
system("stty cooked");
// And we're out of here
return 0;
}
If you are on windows, you can use PeekConsoleInput to detect if there's any input,
HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
DWORD events;
INPUT_RECORD buffer;
PeekConsoleInput( handle, &buffer, 1, &events );
then use ReadConsoleInput to "consume" the input character ..
PeekConsoleInput(handle, &buffer, 1, &events);
if(events > 0)
{
ReadConsoleInput(handle, &buffer, 1, &events);
return buffer.Event.KeyEvent.wVirtualKeyCode;
}
else return 0
to be honest this is from some old code I have, so you have to fiddle a bit with it.
The cool thing though is that it reads input without prompting for anything, so the characters are not displayed at all.
CONIO.H
the functions you need are:
int getch();
Prototype
int _getch(void);
Description
_getch obtains a character from stdin. Input is unbuffered, and this
routine will return as soon as a character is available without
waiting for a carriage return. The character is not echoed to stdout.
_getch bypasses the normal buffering done by getchar and getc. ungetc
cannot be used with _getch.
Synonym
Function: getch
int kbhit();
Description
Checks if a keyboard key has been pressed but not yet read.
Return Value
Returns a non-zero value if a key was pressed. Otherwise, returns 0.
libconio
http://sourceforge.net/projects/libconio
or
Linux c++ implementation of conio.h
http://sourceforge.net/projects/linux-conioh
#include <conio.h>
if (kbhit() != 0) {
cout << getch() << endl;
}
This uses kbhit() to check if the keyboard is being pressed and uses getch() to get the character that is being pressed.
I use kbhit() to see if a char is present and then getchar() to read the data.
On windows, you can use "conio.h". On linux, you will have to implement your own kbhit().
See code below:
// kbhit
#include <stdio.h>
#include <sys/ioctl.h> // For FIONREAD
#include <termios.h>
#include <stdbool.h>
int kbhit(void) {
static bool initflag = false;
static const int STDIN = 0;
if (!initflag) {
// Use termios to turn off line buffering
struct termios term;
tcgetattr(STDIN, &term);
term.c_lflag &= ~ICANON;
tcsetattr(STDIN, TCSANOW, &term);
setbuf(stdin, NULL);
initflag = true;
}
int nbbytes;
ioctl(STDIN, FIONREAD, &nbbytes); // 0 is STDIN
return nbbytes;
}
// main
#include <unistd.h>
int main(int argc, char** argv) {
char c;
//setbuf(stdout, NULL); // Optional: No buffering.
//setbuf(stdin, NULL); // Optional: No buffering.
printf("Press key");
while (!kbhit()) {
printf(".");
fflush(stdout);
sleep(1);
}
c = getchar();
printf("\nChar received:%c\n", c);
printf("Done.\n");
return 0;
}
ncurses provides a nice way to do this!
Also this is my very first post (that I can remember), so any comments at all are welcome. I will appreciate useful ones, but all are welcome!
to compile: g++ -std=c++11 -pthread -lncurses .cpp -o
#include <iostream>
#include <ncurses.h>
#include <future>
char get_keyboard_input();
int main(int argc, char *argv[])
{
initscr();
raw();
noecho();
keypad(stdscr,true);
auto f = std::async(std::launch::async, get_keyboard_input);
while (f.wait_for(std::chrono::milliseconds(20)) != std::future_status::ready)
{
// do some work
}
endwin();
std::cout << "returned: " << f.get() << std::endl;
return 0;
}
char get_keyboard_input()
{
char input = '0';
while(input != 'q')
{
input = getch();
}
return input;
}
Since previous solutions here don't work cross-platform and have trouble with special keys, here is my solution that works on both Windows and Linux and uses a minimum of external libraries (Windows.h for Windows and sys/ioctl.h+termios.h for Linux).
For ASCII characters (newline/tab/space/backspace/delete, !"#$%&'()*+,-./0-9:;<=>?#A-Z[]^_`a-z{|}~üäÄöÖÜßµ´§°¹³²), the ASCII codes (positive numbers) are returned and for special keys (arrow keys, page up/down, pos1/end, escape, insert, F1-F12), the negative of Windows Virtual-Key codes (negative numbers) are returned.
#include <iostream>
#include <string>
#include <thread> // contains <chrono>
using namespace std;
void println(const string& s="") {
cout << s << endl;
}
void sleep(const double t) {
if(t>0.0) this_thread::sleep_for(chrono::milliseconds((int)(1E3*t+0.5)));
}
// ASCII codes (key>0): 8 backspace, 9 tab, 10 newline, 27 escape, 127 delete, !"#$%&'()*+,-./0-9:;<=>?#A-Z[]^_`a-z{|}~üäÄöÖÜßµ´§°¹³²
// control key codes (key<0): -38/-40/-37/-39 up/down/left/right arrow, -33/-34 page up/down, -36/-35 pos1/end
// other key codes (key<0): -45 insert, -144 num lock, -20 caps lock, -91 windows key, -93 kontext menu key, -112 to -123 F1 to F12
// not working: ¹ (251), num lock (-144), caps lock (-20), windows key (-91), kontext menu key (-93), F11 (-122)
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#include <Windows.h>
int key_press() { // not working: F11 (-122, toggles fullscreen)
KEY_EVENT_RECORD keyevent;
INPUT_RECORD irec;
DWORD events;
while(true) {
ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &irec, 1, &events);
if(irec.EventType==KEY_EVENT&&((KEY_EVENT_RECORD&)irec.Event).bKeyDown) {
keyevent = (KEY_EVENT_RECORD&)irec.Event;
const int ca = (int)keyevent.uChar.AsciiChar;
const int cv = (int)keyevent.wVirtualKeyCode;
const int key = ca==0 ? -cv : ca+(ca>0?0:256);
switch(key) {
case -16: continue; // disable Shift
case -17: continue; // disable Ctrl / AltGr
case -18: continue; // disable Alt / AltGr
case -220: continue; // disable first detection of "^" key (not "^" symbol)
case -221: continue; // disable first detection of "`" key (not "`" symbol)
case -191: continue; // disable AltGr + "#"
case -52: continue; // disable AltGr + "4"
case -53: continue; // disable AltGr + "5"
case -54: continue; // disable AltGr + "6"
case -12: continue; // disable num block 5 with num lock deactivated
case 13: return 10; // enter
case -46: return 127; // delete
case -49: return 251; // ¹
case 0: continue;
case 1: continue; // disable Ctrl + a (selects all text)
case 2: continue; // disable Ctrl + b
case 3: continue; // disable Ctrl + c (terminates program)
case 4: continue; // disable Ctrl + d
case 5: continue; // disable Ctrl + e
case 6: continue; // disable Ctrl + f (opens search)
case 7: continue; // disable Ctrl + g
//case 8: continue; // disable Ctrl + h (ascii for backspace)
//case 9: continue; // disable Ctrl + i (ascii for tab)
case 10: continue; // disable Ctrl + j
case 11: continue; // disable Ctrl + k
case 12: continue; // disable Ctrl + l
//case 13: continue; // disable Ctrl + m (breaks console, ascii for new line)
case 14: continue; // disable Ctrl + n
case 15: continue; // disable Ctrl + o
case 16: continue; // disable Ctrl + p
case 17: continue; // disable Ctrl + q
case 18: continue; // disable Ctrl + r
case 19: continue; // disable Ctrl + s
case 20: continue; // disable Ctrl + t
case 21: continue; // disable Ctrl + u
case 22: continue; // disable Ctrl + v (inserts clipboard)
case 23: continue; // disable Ctrl + w
case 24: continue; // disable Ctrl + x
case 25: continue; // disable Ctrl + y
case 26: continue; // disable Ctrl + z
default: return key; // any other ASCII/virtual character
}
}
}
}
#elif defined(__linux__)
#include <sys/ioctl.h>
#include <termios.h>
int key_press() { // not working: ¹ (251), num lock (-144), caps lock (-20), windows key (-91), kontext menu key (-93)
struct termios term;
tcgetattr(0, &term);
while(true) {
term.c_lflag &= ~(ICANON|ECHO); // turn off line buffering and echoing
tcsetattr(0, TCSANOW, &term);
int nbbytes;
ioctl(0, FIONREAD, &nbbytes); // 0 is STDIN
while(!nbbytes) {
sleep(0.01);
fflush(stdout);
ioctl(0, FIONREAD, &nbbytes); // 0 is STDIN
}
int key = (int)getchar();
if(key==27||key==194||key==195) { // escape, 194/195 is escape for °ß´äöüÄÖÜ
key = (int)getchar();
if(key==91) { // [ following escape
key = (int)getchar(); // get code of next char after \e[
if(key==49) { // F5-F8
key = 62+(int)getchar(); // 53, 55-57
if(key==115) key++; // F5 code is too low by 1
getchar(); // take in following ~ (126), but discard code
} else if(key==50) { // insert or F9-F12
key = (int)getchar();
if(key==126) { // insert
key = 45;
} else { // F9-F12
key += 71; // 48, 49, 51, 52
if(key<121) key++; // F11 and F12 are too low by 1
getchar(); // take in following ~ (126), but discard code
}
} else if(key==51||key==53||key==54) { // delete, page up/down
getchar(); // take in following ~ (126), but discard code
}
} else if(key==79) { // F1-F4
key = 32+(int)getchar(); // 80-83
}
key = -key; // use negative numbers for escaped keys
}
term.c_lflag |= (ICANON|ECHO); // turn on line buffering and echoing
tcsetattr(0, TCSANOW, &term);
switch(key) {
case 127: return 8; // backspace
case -27: return 27; // escape
case -51: return 127; // delete
case -164: return 132; // ä
case -182: return 148; // ö
case -188: return 129; // ü
case -132: return 142; // Ä
case -150: return 153; // Ö
case -156: return 154; // Ü
case -159: return 225; // ß
case -181: return 230; // µ
case -167: return 245; // §
case -176: return 248; // °
case -178: return 253; // ²
case -179: return 252; // ³
case -180: return 239; // ´
case -65: return -38; // up arrow
case -66: return -40; // down arrow
case -68: return -37; // left arrow
case -67: return -39; // right arrow
case -53: return -33; // page up
case -54: return -34; // page down
case -72: return -36; // pos1
case -70: return -35; // end
case 0: continue;
case 1: continue; // disable Ctrl + a
case 2: continue; // disable Ctrl + b
case 3: continue; // disable Ctrl + c (terminates program)
case 4: continue; // disable Ctrl + d
case 5: continue; // disable Ctrl + e
case 6: continue; // disable Ctrl + f
case 7: continue; // disable Ctrl + g
case 8: continue; // disable Ctrl + h
//case 9: continue; // disable Ctrl + i (ascii for tab)
//case 10: continue; // disable Ctrl + j (ascii for new line)
case 11: continue; // disable Ctrl + k
case 12: continue; // disable Ctrl + l
case 13: continue; // disable Ctrl + m
case 14: continue; // disable Ctrl + n
case 15: continue; // disable Ctrl + o
case 16: continue; // disable Ctrl + p
case 17: continue; // disable Ctrl + q
case 18: continue; // disable Ctrl + r
case 19: continue; // disable Ctrl + s
case 20: continue; // disable Ctrl + t
case 21: continue; // disable Ctrl + u
case 22: continue; // disable Ctrl + v
case 23: continue; // disable Ctrl + w
case 24: continue; // disable Ctrl + x
case 25: continue; // disable Ctrl + y
case 26: continue; // disable Ctrl + z (terminates program)
default: return key; // any other ASCII character
}
}
}
#endif // Windows/Linux
Finally, here is an example on how to use it:
int main() {
while(true) {
const int key = key_press(); // blocks until a key is pressed
println("Input is: "+to_string(key)+", \""+(char)key+"\"");
}
return 0;
}
Assuming Windows, take a look at the ReadConsoleInput function.
C and C++ take a very abstract view of I/O, and there is no standard way of doing what you want. There are standard ways to get characters from the standard input stream, if there are any to get, and nothing else is defined by either language. Any answer will therefore have to be platform-specific, perhaps depending not only on the operating system but also the software framework.
There's some reasonable guesses here, but there's no way to answer your question without knowing what your target environment is.
The closest thing to portable is to use the ncurses library to put the terminal into "cbreak mode". The API is gigantic; the routines you'll want most are
initscr and endwin
cbreak and nocbreak
getch
Good luck!
I always wanted a loop to read my input without pressing return key.
this worked for me.
#include<stdio.h>
main()
{
char ch;
system("stty raw");//seting the terminal in raw mode
while(1)
{
ch=getchar();
if(ch=='~'){ //terminate or come out of raw mode on "~" pressed
system("stty cooked");
//while(1);//you may still run the code
exit(0); //or terminate
}
printf("you pressed %c\n ",ch); //write rest code here
}
}
works for me on windows:
#include <conio.h>
char c = _getch();
The following is a solution extracted from Expert C Programming: Deep Secrets, which is supposed to work on SVr4. It uses stty and ioctl.
#include <sys/filio.h>
int kbhit()
{
int i;
ioctl(0, FIONREAD, &i);
return i; /* return a count of chars available to read */
}
main()
{
int i = 0;
intc='';
system("stty raw -echo");
printf("enter 'q' to quit \n");
for (;c!='q';i++) {
if (kbhit()) {
c=getchar();
printf("\n got %c, on iteration %d",c, i);
}
}
system("stty cooked echo");
}
You can do it portably using SDL (the Simple DirectMedia Library), though I suspect you may not like its behavior. When I tried it, I had to have SDL create a new video window (even though I didn't need it for my program) and have this window "grab" almost all keyboard and mouse input (which was okay for my usage but could be annoying or unworkable in other situations). I suspect it's overkill and not worth it unless complete portability is a must--otherwise try one of the other suggested solutions.
By the way, this will give you key press and release events separately, if you're into that.
Variant of ssinfod's answer for Linux that's a little cleaner for my tastes, implemented for wcout and wchar_t, and erases invalid characters without bugs.
#include <functional>
//For Linux kbhit(). For Windows, use conio.h.
#ifdef __unix__
#include <sys/ioctl.h> //For FIONREAD.
#include <termios.h>
//Call this at program start to setup for kbhit.
void initTerminalInput()
{
//Disable internal buffering.
std::wcout << std::unitbuf;
//Turn off line buffering.
struct termios term;
tcgetattr(0, &term);
term.c_lflag &= ~ICANON;
tcsetattr(0, TCSANOW, &term);
setbuf(stdin, NULL);
}
//Returns 0 if there's no input character to read.
int kbhit()
{
static int nbbytes;
ioctl(0, FIONREAD, &nbbytes);
return nbbytes;
}
#endif
//Waits for and retrieves a single validated character, calling a validation function on each character entered and
//erasing any that are invalid (when the validation function returns false).
static wchar_t getWChar(std::function<bool(wchar_t)> validationFunction)
{
static wchar_t inputWChar;
do
{
//Wait until there's an input character.
while (!kbhit())
{
}
inputWChar = getwchar();
//Validate the input character.
if (validationFunction(inputWChar))
{
//Valid.
break;
}
else
{
//Erase the invalid character.
std::wcout << L"\b \b";
}
} while (true);
return inputWChar;
}
In the below example, I wanted for the user to enter either 1, 2, or 3. Any other characters entered will not be displayed, and it will wait until one of the valid characters is pressed:
int main()
{
#ifdef __unix__
initTerminalInput();
#endif
getWChar([] (wchar_t inputWChar)
{
return (inputWChar >= L'1' && inputWChar <= L'3');
});
return 0;
}
Here's a version that doesn't shell out to the system (written and tested on macOS 10.14)
#include <unistd.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
char* getStr( char* buffer , int maxRead ) {
int numRead = 0;
char ch;
struct termios old = {0};
struct termios new = {0};
if( tcgetattr( 0 , &old ) < 0 ) perror( "tcgetattr() old settings" );
if( tcgetattr( 0 , &new ) < 0 ) perror( "tcgetaart() new settings" );
cfmakeraw( &new );
if( tcsetattr( 0 , TCSADRAIN , &new ) < 0 ) perror( "tcssetattr makeraw new" );
for( int i = 0 ; i < maxRead ; i++) {
ch = getchar();
switch( ch ) {
case EOF:
case '\n':
case '\r':
goto exit_getStr;
break;
default:
printf( "%1c" , ch );
buffer[ numRead++ ] = ch;
if( numRead >= maxRead ) {
goto exit_getStr;
}
break;
}
}
exit_getStr:
if( tcsetattr( 0 , TCSADRAIN , &old) < 0) perror ("tcsetattr reset to old" );
printf( "\n" );
return buffer;
}
int main( void )
{
const int maxChars = 20;
char stringBuffer[ maxChars+1 ];
memset( stringBuffer , 0 , maxChars+1 ); // initialize to 0
printf( "enter a string: ");
getStr( stringBuffer , maxChars );
printf( "you entered: [%s]\n" , stringBuffer );
}
ON WINDOWS I do so:
#include <Windows.h>
int main()
{
system("PAUSE>NUL");
if (GetKeyState(0x41/*(the 'A' key, choosen e.g.)*/) & (0xff80/*That stands for "Default state / Key Down"*/)) {
//whatever I want to do after 'A' is pressed
}
}
Here can be found a list of keys with its own hex value for each:
https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
I was running into this same issue. Here is a small solution for windows console using cygwin g++ with if(GetKeyState(keycode) & bitANDcompare){};.
#include <windows.h>
#include <fstream>
#include <iostream>
using namespace std;
void clear() {
COORD topLeft = { 0, 0 };
HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO screen;
DWORD written;
GetConsoleScreenBufferInfo(console, &screen);
FillConsoleOutputCharacterA(
console, ' ', screen.dwSize.X * screen.dwSize.Y, topLeft, &written
);
FillConsoleOutputAttribute(
console, FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE,
screen.dwSize.X * screen.dwSize.Y, topLeft, &written
);
SetConsoleCursorPosition(console, topLeft);
}
class Keyclick{
private:
int key;
char id;
public:
bool keydown = false;
Keyclick(int key1, char id1){
key=key1;
id=id1;
};
void watch(){
if(keydown==false){
if(GetKeyState(key) & 0x8000 ){
cout << id;
cout << " pressed.\r\n";
keydown = true;
}
}
if(keydown == true){
if(!(GetKeyState(key) & 0x8000)) {
cout << "released!!!!!!!!!!\r\n";
keydown = false;
clear();
}
}
};
};
int main()
{
bool primaryloop =true;
Keyclick keysp(VK_SPACE,'S');
Keyclick keyw(0x57,'w');
Keyclick keya(0x41,'a');
Keyclick keys(0x53,'s');
Keyclick keyd(0x44,'d');
Keyclick keyesc(VK_ESCAPE,'E');
while(primaryloop){
keysp.watch();
keyw.watch();
keya.watch();
keys.watch();
keyd.watch();
keyesc.watch();
if(keyesc.keydown){
primaryloop=false;
};
}
return 0;
}
https://github.com/wark77/windows_console_keypoller/blob/main/getkeystate_SOLUTION01.cpp
Pipe demo: how to read keyboard presses from a system call pipe in C
Also ideally it wouldn't echo the input character to the screen. I just want to capture keystrokes with out effecting the console screen.
To do this on Linux you can use the following bash command:
read -sn1 c && printf "You Pressed: %s\n" "$c"
See my answer here for details on this: shell script respond to keypress.
So, to do this in C or C++ on Linux, you simply need to call the above bash command via a system call through a pipe with popen() and fgets() so you can read the output from the bash command.
Here is a full example which runs fine on Linux in both C and C++:
read_system_call_via_pipe__keypress.c:
#include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
#include <stdint.h> // For `uint8_t`, `int8_t`, etc.
#include <stdio.h> // For `printf()`
#include <stdlib.h>
#define BUFSIZE 32
// Read a keyboard key press and return the character pressed, or a negative
// number in the event of an error.
// NB: for help reading output from system calls, see here:
// 1. https://stackoverflow.com/a/28971647/4561887
// 2. https://stackoverflow.com/a/18297075/4561887
char getKeypress()
{
// This bash cmd is from my answer here:
// https://stackoverflow.com/a/70979348/4561887
const char* cmd = "bash -c 'read -s -n1 c && printf \"%s\" \"$c\"'";
FILE *fp = popen(cmd, "r");
if (fp == NULL)
{
printf("\nError opening pipe!\n");
return -1;
}
char buf[BUFSIZE] = {0};
char* retval1 = fgets(buf, BUFSIZE, fp);
if (retval1 == NULL)
{
printf("\nFailed to read cmd response.\n");
return -2;
}
// See meaning of this return value here:
// https://stackoverflow.com/questions/43116/how-can-i-run-an-external-program-from-c-and-parse-its-output/28971647#comment60311936_28971647
int retval2 = pclose(fp);
if (retval2 == -1)
{
printf("\nError obtaining the cmd's exit status code.\n");
return -3;
}
else if (retval2 != 0)
{
printf("\nCommand exited with exit status code %i.\n", retval2);
return -4;
}
char keyPressed = buf[0];
return keyPressed;
}
// int main(int argc, char *argv[]) // alternative prototype
int main()
{
printf("Press any key to continue: ");
fflush(stdout);
char keyPressed = getKeypress();
if (keyPressed > 0)
{
printf("\nKey pressed = %c\n", keyPressed);
}
return 0;
}
The C and C++ compile and run commands are part of the output below. Here are some demos:
In C:
eRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=gnu17 read_keypress_system_call.c -o bin/a && bin/a
Press any key to continue:
Key pressed = P
OR, in C++:
eRCaGuy_hello_world/c$ g++ -Wall -Wextra -Werror -O3 -std=c++17 read_keypress_system_call.c -o bin/a && bin/a
Press any key to continue:
Key pressed = u
See also:
To take this one step further, see my other answer here where I detect and parse 3 chars at a time in order to detect arrow key presses Up, Down, Left, or Right: Read Key pressings in C ex. Arrow keys, Enter key
References:
How I learned to read from a pipe to get system call output:
How can I run an external program from C and parse its output?
Is there a way to obtain the output of a linux command(like ifconfig) on a .txt file using a C program? [duplicate]
How to compile and use popen() in C: use -std=gnu17 instead of -std=c17: popen implicitly declared even though #include <stdio.h> is added
[my answer] How to read without blocking, via bash: shell script respond to keypress
[my answer] How do I read in the Enter key as an input in C?
All 3 Q&As on this topic
Capture characters from standard input without waiting for enter to be pressed
C non-blocking keyboard input
How to avoid pressing Enter with getchar() for reading a single character only?
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.
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:
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++.
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.
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).
I found this on another forum while looking to solve the same problem. I've modified it a bit from what I found. It works great. I'm running OS X, so if you're running Microsoft, you'll need to find the correct system() command to switch to raw and cooked modes.
#include <iostream>
#include <stdio.h>
using namespace std;
int main() {
// Output prompt
cout << "Press any key to continue..." << endl;
// Set terminal to raw mode
system("stty raw");
// Wait for single character
char input = getchar();
// Echo input:
cout << "--" << input << "--";
// Reset terminal to normal "cooked" mode
system("stty cooked");
// And we're out of here
return 0;
}
If you are on windows, you can use PeekConsoleInput to detect if there's any input,
HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
DWORD events;
INPUT_RECORD buffer;
PeekConsoleInput( handle, &buffer, 1, &events );
then use ReadConsoleInput to "consume" the input character ..
PeekConsoleInput(handle, &buffer, 1, &events);
if(events > 0)
{
ReadConsoleInput(handle, &buffer, 1, &events);
return buffer.Event.KeyEvent.wVirtualKeyCode;
}
else return 0
to be honest this is from some old code I have, so you have to fiddle a bit with it.
The cool thing though is that it reads input without prompting for anything, so the characters are not displayed at all.
CONIO.H
the functions you need are:
int getch();
Prototype
int _getch(void);
Description
_getch obtains a character from stdin. Input is unbuffered, and this
routine will return as soon as a character is available without
waiting for a carriage return. The character is not echoed to stdout.
_getch bypasses the normal buffering done by getchar and getc. ungetc
cannot be used with _getch.
Synonym
Function: getch
int kbhit();
Description
Checks if a keyboard key has been pressed but not yet read.
Return Value
Returns a non-zero value if a key was pressed. Otherwise, returns 0.
libconio
http://sourceforge.net/projects/libconio
or
Linux c++ implementation of conio.h
http://sourceforge.net/projects/linux-conioh
#include <conio.h>
if (kbhit() != 0) {
cout << getch() << endl;
}
This uses kbhit() to check if the keyboard is being pressed and uses getch() to get the character that is being pressed.
I use kbhit() to see if a char is present and then getchar() to read the data.
On windows, you can use "conio.h". On linux, you will have to implement your own kbhit().
See code below:
// kbhit
#include <stdio.h>
#include <sys/ioctl.h> // For FIONREAD
#include <termios.h>
#include <stdbool.h>
int kbhit(void) {
static bool initflag = false;
static const int STDIN = 0;
if (!initflag) {
// Use termios to turn off line buffering
struct termios term;
tcgetattr(STDIN, &term);
term.c_lflag &= ~ICANON;
tcsetattr(STDIN, TCSANOW, &term);
setbuf(stdin, NULL);
initflag = true;
}
int nbbytes;
ioctl(STDIN, FIONREAD, &nbbytes); // 0 is STDIN
return nbbytes;
}
// main
#include <unistd.h>
int main(int argc, char** argv) {
char c;
//setbuf(stdout, NULL); // Optional: No buffering.
//setbuf(stdin, NULL); // Optional: No buffering.
printf("Press key");
while (!kbhit()) {
printf(".");
fflush(stdout);
sleep(1);
}
c = getchar();
printf("\nChar received:%c\n", c);
printf("Done.\n");
return 0;
}
ncurses provides a nice way to do this!
Also this is my very first post (that I can remember), so any comments at all are welcome. I will appreciate useful ones, but all are welcome!
to compile: g++ -std=c++11 -pthread -lncurses .cpp -o
#include <iostream>
#include <ncurses.h>
#include <future>
char get_keyboard_input();
int main(int argc, char *argv[])
{
initscr();
raw();
noecho();
keypad(stdscr,true);
auto f = std::async(std::launch::async, get_keyboard_input);
while (f.wait_for(std::chrono::milliseconds(20)) != std::future_status::ready)
{
// do some work
}
endwin();
std::cout << "returned: " << f.get() << std::endl;
return 0;
}
char get_keyboard_input()
{
char input = '0';
while(input != 'q')
{
input = getch();
}
return input;
}
Since previous solutions here don't work cross-platform and have trouble with special keys, here is my solution that works on both Windows and Linux and uses a minimum of external libraries (Windows.h for Windows and sys/ioctl.h+termios.h for Linux).
For ASCII characters (newline/tab/space/backspace/delete, !"#$%&'()*+,-./0-9:;<=>?#A-Z[]^_`a-z{|}~üäÄöÖÜßµ´§°¹³²), the ASCII codes (positive numbers) are returned and for special keys (arrow keys, page up/down, pos1/end, escape, insert, F1-F12), the negative of Windows Virtual-Key codes (negative numbers) are returned.
#include <iostream>
#include <string>
#include <thread> // contains <chrono>
using namespace std;
void println(const string& s="") {
cout << s << endl;
}
void sleep(const double t) {
if(t>0.0) this_thread::sleep_for(chrono::milliseconds((int)(1E3*t+0.5)));
}
// ASCII codes (key>0): 8 backspace, 9 tab, 10 newline, 27 escape, 127 delete, !"#$%&'()*+,-./0-9:;<=>?#A-Z[]^_`a-z{|}~üäÄöÖÜßµ´§°¹³²
// control key codes (key<0): -38/-40/-37/-39 up/down/left/right arrow, -33/-34 page up/down, -36/-35 pos1/end
// other key codes (key<0): -45 insert, -144 num lock, -20 caps lock, -91 windows key, -93 kontext menu key, -112 to -123 F1 to F12
// not working: ¹ (251), num lock (-144), caps lock (-20), windows key (-91), kontext menu key (-93), F11 (-122)
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#include <Windows.h>
int key_press() { // not working: F11 (-122, toggles fullscreen)
KEY_EVENT_RECORD keyevent;
INPUT_RECORD irec;
DWORD events;
while(true) {
ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &irec, 1, &events);
if(irec.EventType==KEY_EVENT&&((KEY_EVENT_RECORD&)irec.Event).bKeyDown) {
keyevent = (KEY_EVENT_RECORD&)irec.Event;
const int ca = (int)keyevent.uChar.AsciiChar;
const int cv = (int)keyevent.wVirtualKeyCode;
const int key = ca==0 ? -cv : ca+(ca>0?0:256);
switch(key) {
case -16: continue; // disable Shift
case -17: continue; // disable Ctrl / AltGr
case -18: continue; // disable Alt / AltGr
case -220: continue; // disable first detection of "^" key (not "^" symbol)
case -221: continue; // disable first detection of "`" key (not "`" symbol)
case -191: continue; // disable AltGr + "#"
case -52: continue; // disable AltGr + "4"
case -53: continue; // disable AltGr + "5"
case -54: continue; // disable AltGr + "6"
case -12: continue; // disable num block 5 with num lock deactivated
case 13: return 10; // enter
case -46: return 127; // delete
case -49: return 251; // ¹
case 0: continue;
case 1: continue; // disable Ctrl + a (selects all text)
case 2: continue; // disable Ctrl + b
case 3: continue; // disable Ctrl + c (terminates program)
case 4: continue; // disable Ctrl + d
case 5: continue; // disable Ctrl + e
case 6: continue; // disable Ctrl + f (opens search)
case 7: continue; // disable Ctrl + g
//case 8: continue; // disable Ctrl + h (ascii for backspace)
//case 9: continue; // disable Ctrl + i (ascii for tab)
case 10: continue; // disable Ctrl + j
case 11: continue; // disable Ctrl + k
case 12: continue; // disable Ctrl + l
//case 13: continue; // disable Ctrl + m (breaks console, ascii for new line)
case 14: continue; // disable Ctrl + n
case 15: continue; // disable Ctrl + o
case 16: continue; // disable Ctrl + p
case 17: continue; // disable Ctrl + q
case 18: continue; // disable Ctrl + r
case 19: continue; // disable Ctrl + s
case 20: continue; // disable Ctrl + t
case 21: continue; // disable Ctrl + u
case 22: continue; // disable Ctrl + v (inserts clipboard)
case 23: continue; // disable Ctrl + w
case 24: continue; // disable Ctrl + x
case 25: continue; // disable Ctrl + y
case 26: continue; // disable Ctrl + z
default: return key; // any other ASCII/virtual character
}
}
}
}
#elif defined(__linux__)
#include <sys/ioctl.h>
#include <termios.h>
int key_press() { // not working: ¹ (251), num lock (-144), caps lock (-20), windows key (-91), kontext menu key (-93)
struct termios term;
tcgetattr(0, &term);
while(true) {
term.c_lflag &= ~(ICANON|ECHO); // turn off line buffering and echoing
tcsetattr(0, TCSANOW, &term);
int nbbytes;
ioctl(0, FIONREAD, &nbbytes); // 0 is STDIN
while(!nbbytes) {
sleep(0.01);
fflush(stdout);
ioctl(0, FIONREAD, &nbbytes); // 0 is STDIN
}
int key = (int)getchar();
if(key==27||key==194||key==195) { // escape, 194/195 is escape for °ß´äöüÄÖÜ
key = (int)getchar();
if(key==91) { // [ following escape
key = (int)getchar(); // get code of next char after \e[
if(key==49) { // F5-F8
key = 62+(int)getchar(); // 53, 55-57
if(key==115) key++; // F5 code is too low by 1
getchar(); // take in following ~ (126), but discard code
} else if(key==50) { // insert or F9-F12
key = (int)getchar();
if(key==126) { // insert
key = 45;
} else { // F9-F12
key += 71; // 48, 49, 51, 52
if(key<121) key++; // F11 and F12 are too low by 1
getchar(); // take in following ~ (126), but discard code
}
} else if(key==51||key==53||key==54) { // delete, page up/down
getchar(); // take in following ~ (126), but discard code
}
} else if(key==79) { // F1-F4
key = 32+(int)getchar(); // 80-83
}
key = -key; // use negative numbers for escaped keys
}
term.c_lflag |= (ICANON|ECHO); // turn on line buffering and echoing
tcsetattr(0, TCSANOW, &term);
switch(key) {
case 127: return 8; // backspace
case -27: return 27; // escape
case -51: return 127; // delete
case -164: return 132; // ä
case -182: return 148; // ö
case -188: return 129; // ü
case -132: return 142; // Ä
case -150: return 153; // Ö
case -156: return 154; // Ü
case -159: return 225; // ß
case -181: return 230; // µ
case -167: return 245; // §
case -176: return 248; // °
case -178: return 253; // ²
case -179: return 252; // ³
case -180: return 239; // ´
case -65: return -38; // up arrow
case -66: return -40; // down arrow
case -68: return -37; // left arrow
case -67: return -39; // right arrow
case -53: return -33; // page up
case -54: return -34; // page down
case -72: return -36; // pos1
case -70: return -35; // end
case 0: continue;
case 1: continue; // disable Ctrl + a
case 2: continue; // disable Ctrl + b
case 3: continue; // disable Ctrl + c (terminates program)
case 4: continue; // disable Ctrl + d
case 5: continue; // disable Ctrl + e
case 6: continue; // disable Ctrl + f
case 7: continue; // disable Ctrl + g
case 8: continue; // disable Ctrl + h
//case 9: continue; // disable Ctrl + i (ascii for tab)
//case 10: continue; // disable Ctrl + j (ascii for new line)
case 11: continue; // disable Ctrl + k
case 12: continue; // disable Ctrl + l
case 13: continue; // disable Ctrl + m
case 14: continue; // disable Ctrl + n
case 15: continue; // disable Ctrl + o
case 16: continue; // disable Ctrl + p
case 17: continue; // disable Ctrl + q
case 18: continue; // disable Ctrl + r
case 19: continue; // disable Ctrl + s
case 20: continue; // disable Ctrl + t
case 21: continue; // disable Ctrl + u
case 22: continue; // disable Ctrl + v
case 23: continue; // disable Ctrl + w
case 24: continue; // disable Ctrl + x
case 25: continue; // disable Ctrl + y
case 26: continue; // disable Ctrl + z (terminates program)
default: return key; // any other ASCII character
}
}
}
#endif // Windows/Linux
Finally, here is an example on how to use it:
int main() {
while(true) {
const int key = key_press(); // blocks until a key is pressed
println("Input is: "+to_string(key)+", \""+(char)key+"\"");
}
return 0;
}
Assuming Windows, take a look at the ReadConsoleInput function.
C and C++ take a very abstract view of I/O, and there is no standard way of doing what you want. There are standard ways to get characters from the standard input stream, if there are any to get, and nothing else is defined by either language. Any answer will therefore have to be platform-specific, perhaps depending not only on the operating system but also the software framework.
There's some reasonable guesses here, but there's no way to answer your question without knowing what your target environment is.
The closest thing to portable is to use the ncurses library to put the terminal into "cbreak mode". The API is gigantic; the routines you'll want most are
initscr and endwin
cbreak and nocbreak
getch
Good luck!
I always wanted a loop to read my input without pressing return key.
this worked for me.
#include<stdio.h>
main()
{
char ch;
system("stty raw");//seting the terminal in raw mode
while(1)
{
ch=getchar();
if(ch=='~'){ //terminate or come out of raw mode on "~" pressed
system("stty cooked");
//while(1);//you may still run the code
exit(0); //or terminate
}
printf("you pressed %c\n ",ch); //write rest code here
}
}
works for me on windows:
#include <conio.h>
char c = _getch();
The following is a solution extracted from Expert C Programming: Deep Secrets, which is supposed to work on SVr4. It uses stty and ioctl.
#include <sys/filio.h>
int kbhit()
{
int i;
ioctl(0, FIONREAD, &i);
return i; /* return a count of chars available to read */
}
main()
{
int i = 0;
intc='';
system("stty raw -echo");
printf("enter 'q' to quit \n");
for (;c!='q';i++) {
if (kbhit()) {
c=getchar();
printf("\n got %c, on iteration %d",c, i);
}
}
system("stty cooked echo");
}
You can do it portably using SDL (the Simple DirectMedia Library), though I suspect you may not like its behavior. When I tried it, I had to have SDL create a new video window (even though I didn't need it for my program) and have this window "grab" almost all keyboard and mouse input (which was okay for my usage but could be annoying or unworkable in other situations). I suspect it's overkill and not worth it unless complete portability is a must--otherwise try one of the other suggested solutions.
By the way, this will give you key press and release events separately, if you're into that.
Variant of ssinfod's answer for Linux that's a little cleaner for my tastes, implemented for wcout and wchar_t, and erases invalid characters without bugs.
#include <functional>
//For Linux kbhit(). For Windows, use conio.h.
#ifdef __unix__
#include <sys/ioctl.h> //For FIONREAD.
#include <termios.h>
//Call this at program start to setup for kbhit.
void initTerminalInput()
{
//Disable internal buffering.
std::wcout << std::unitbuf;
//Turn off line buffering.
struct termios term;
tcgetattr(0, &term);
term.c_lflag &= ~ICANON;
tcsetattr(0, TCSANOW, &term);
setbuf(stdin, NULL);
}
//Returns 0 if there's no input character to read.
int kbhit()
{
static int nbbytes;
ioctl(0, FIONREAD, &nbbytes);
return nbbytes;
}
#endif
//Waits for and retrieves a single validated character, calling a validation function on each character entered and
//erasing any that are invalid (when the validation function returns false).
static wchar_t getWChar(std::function<bool(wchar_t)> validationFunction)
{
static wchar_t inputWChar;
do
{
//Wait until there's an input character.
while (!kbhit())
{
}
inputWChar = getwchar();
//Validate the input character.
if (validationFunction(inputWChar))
{
//Valid.
break;
}
else
{
//Erase the invalid character.
std::wcout << L"\b \b";
}
} while (true);
return inputWChar;
}
In the below example, I wanted for the user to enter either 1, 2, or 3. Any other characters entered will not be displayed, and it will wait until one of the valid characters is pressed:
int main()
{
#ifdef __unix__
initTerminalInput();
#endif
getWChar([] (wchar_t inputWChar)
{
return (inputWChar >= L'1' && inputWChar <= L'3');
});
return 0;
}
Here's a version that doesn't shell out to the system (written and tested on macOS 10.14)
#include <unistd.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
char* getStr( char* buffer , int maxRead ) {
int numRead = 0;
char ch;
struct termios old = {0};
struct termios new = {0};
if( tcgetattr( 0 , &old ) < 0 ) perror( "tcgetattr() old settings" );
if( tcgetattr( 0 , &new ) < 0 ) perror( "tcgetaart() new settings" );
cfmakeraw( &new );
if( tcsetattr( 0 , TCSADRAIN , &new ) < 0 ) perror( "tcssetattr makeraw new" );
for( int i = 0 ; i < maxRead ; i++) {
ch = getchar();
switch( ch ) {
case EOF:
case '\n':
case '\r':
goto exit_getStr;
break;
default:
printf( "%1c" , ch );
buffer[ numRead++ ] = ch;
if( numRead >= maxRead ) {
goto exit_getStr;
}
break;
}
}
exit_getStr:
if( tcsetattr( 0 , TCSADRAIN , &old) < 0) perror ("tcsetattr reset to old" );
printf( "\n" );
return buffer;
}
int main( void )
{
const int maxChars = 20;
char stringBuffer[ maxChars+1 ];
memset( stringBuffer , 0 , maxChars+1 ); // initialize to 0
printf( "enter a string: ");
getStr( stringBuffer , maxChars );
printf( "you entered: [%s]\n" , stringBuffer );
}
ON WINDOWS I do so:
#include <Windows.h>
int main()
{
system("PAUSE>NUL");
if (GetKeyState(0x41/*(the 'A' key, choosen e.g.)*/) & (0xff80/*That stands for "Default state / Key Down"*/)) {
//whatever I want to do after 'A' is pressed
}
}
Here can be found a list of keys with its own hex value for each:
https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
I was running into this same issue. Here is a small solution for windows console using cygwin g++ with if(GetKeyState(keycode) & bitANDcompare){};.
#include <windows.h>
#include <fstream>
#include <iostream>
using namespace std;
void clear() {
COORD topLeft = { 0, 0 };
HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO screen;
DWORD written;
GetConsoleScreenBufferInfo(console, &screen);
FillConsoleOutputCharacterA(
console, ' ', screen.dwSize.X * screen.dwSize.Y, topLeft, &written
);
FillConsoleOutputAttribute(
console, FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE,
screen.dwSize.X * screen.dwSize.Y, topLeft, &written
);
SetConsoleCursorPosition(console, topLeft);
}
class Keyclick{
private:
int key;
char id;
public:
bool keydown = false;
Keyclick(int key1, char id1){
key=key1;
id=id1;
};
void watch(){
if(keydown==false){
if(GetKeyState(key) & 0x8000 ){
cout << id;
cout << " pressed.\r\n";
keydown = true;
}
}
if(keydown == true){
if(!(GetKeyState(key) & 0x8000)) {
cout << "released!!!!!!!!!!\r\n";
keydown = false;
clear();
}
}
};
};
int main()
{
bool primaryloop =true;
Keyclick keysp(VK_SPACE,'S');
Keyclick keyw(0x57,'w');
Keyclick keya(0x41,'a');
Keyclick keys(0x53,'s');
Keyclick keyd(0x44,'d');
Keyclick keyesc(VK_ESCAPE,'E');
while(primaryloop){
keysp.watch();
keyw.watch();
keya.watch();
keys.watch();
keyd.watch();
keyesc.watch();
if(keyesc.keydown){
primaryloop=false;
};
}
return 0;
}
https://github.com/wark77/windows_console_keypoller/blob/main/getkeystate_SOLUTION01.cpp
Pipe demo: how to read keyboard presses from a system call pipe in C
Also ideally it wouldn't echo the input character to the screen. I just want to capture keystrokes with out effecting the console screen.
To do this on Linux you can use the following bash command:
read -sn1 c && printf "You Pressed: %s\n" "$c"
See my answer here for details on this: shell script respond to keypress.
So, to do this in C or C++ on Linux, you simply need to call the above bash command via a system call through a pipe with popen() and fgets() so you can read the output from the bash command.
Here is a full example which runs fine on Linux in both C and C++:
read_system_call_via_pipe__keypress.c:
#include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
#include <stdint.h> // For `uint8_t`, `int8_t`, etc.
#include <stdio.h> // For `printf()`
#include <stdlib.h>
#define BUFSIZE 32
// Read a keyboard key press and return the character pressed, or a negative
// number in the event of an error.
// NB: for help reading output from system calls, see here:
// 1. https://stackoverflow.com/a/28971647/4561887
// 2. https://stackoverflow.com/a/18297075/4561887
char getKeypress()
{
// This bash cmd is from my answer here:
// https://stackoverflow.com/a/70979348/4561887
const char* cmd = "bash -c 'read -s -n1 c && printf \"%s\" \"$c\"'";
FILE *fp = popen(cmd, "r");
if (fp == NULL)
{
printf("\nError opening pipe!\n");
return -1;
}
char buf[BUFSIZE] = {0};
char* retval1 = fgets(buf, BUFSIZE, fp);
if (retval1 == NULL)
{
printf("\nFailed to read cmd response.\n");
return -2;
}
// See meaning of this return value here:
// https://stackoverflow.com/questions/43116/how-can-i-run-an-external-program-from-c-and-parse-its-output/28971647#comment60311936_28971647
int retval2 = pclose(fp);
if (retval2 == -1)
{
printf("\nError obtaining the cmd's exit status code.\n");
return -3;
}
else if (retval2 != 0)
{
printf("\nCommand exited with exit status code %i.\n", retval2);
return -4;
}
char keyPressed = buf[0];
return keyPressed;
}
// int main(int argc, char *argv[]) // alternative prototype
int main()
{
printf("Press any key to continue: ");
fflush(stdout);
char keyPressed = getKeypress();
if (keyPressed > 0)
{
printf("\nKey pressed = %c\n", keyPressed);
}
return 0;
}
The C and C++ compile and run commands are part of the output below. Here are some demos:
In C:
eRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=gnu17 read_keypress_system_call.c -o bin/a && bin/a
Press any key to continue:
Key pressed = P
OR, in C++:
eRCaGuy_hello_world/c$ g++ -Wall -Wextra -Werror -O3 -std=c++17 read_keypress_system_call.c -o bin/a && bin/a
Press any key to continue:
Key pressed = u
See also:
To take this one step further, see my other answer here where I detect and parse 3 chars at a time in order to detect arrow key presses Up, Down, Left, or Right: Read Key pressings in C ex. Arrow keys, Enter key
References:
How I learned to read from a pipe to get system call output:
How can I run an external program from C and parse its output?
Is there a way to obtain the output of a linux command(like ifconfig) on a .txt file using a C program? [duplicate]
How to compile and use popen() in C: use -std=gnu17 instead of -std=c17: popen implicitly declared even though #include <stdio.h> is added
[my answer] How to read without blocking, via bash: shell script respond to keypress
[my answer] How do I read in the Enter key as an input in C?
All 3 Q&As on this topic
Capture characters from standard input without waiting for enter to be pressed
C non-blocking keyboard input
How to avoid pressing Enter with getchar() for reading a single character only?
I want to mask my password while writing it with *.
I use Linux GCC for this code.
I know one solution is to use getch() function like this
#include <conio.h>
int main()
{
char c,password[10];
int i;
while( (c=getch())!= '\n');{
password[i] = c;
printf("*");
i++;
}
return 1;
}
but the problem is that GCC does not include conio.h file so, getch() is useless for me.
Does anyone have a solution?
In the Linux world, masking isn't usually done with asterisks, normally echoing is just turned off and the terminal displays blanks E.g. if you use su or log into a virtual terminal etc.
There is a library function to handle getting passwords, it won't mask the password with asterisks but will disable echoing of the password to terminal. I pulled this out of a linux book I have. I believe its part of the posix standard
#include <unistd.h>
char *getpass(const char *prompt);
/*Returns pointer to statically allocated input password string
on success, or NULL on error*/
The getpass() function first disables echoing and all processing of
terminal special characters (such as the interrupt character, normally
Control-C).
It then prints the string pointed to by prompt, and reads a line of
input, returning the null-terminated input string with the trailing
newline stripped, as its function result.
A google search for getpass() has a reference to the GNU implementation (should be in most linux distros) and some sample code for implementing your own if need be
http://www.gnu.org/s/hello/manual/libc/getpass.html
Their example for rolling your own:
#include <termios.h>
#include <stdio.h>
ssize_t
my_getpass (char **lineptr, size_t *n, FILE *stream)
{
struct termios old, new;
int nread;
/* Turn echoing off and fail if we can't. */
if (tcgetattr (fileno (stream), &old) != 0)
return -1;
new = old;
new.c_lflag &= ~ECHO;
if (tcsetattr (fileno (stream), TCSAFLUSH, &new) != 0)
return -1;
/* Read the password. */
nread = getline (lineptr, n, stream);
/* Restore terminal. */
(void) tcsetattr (fileno (stream), TCSAFLUSH, &old);
return nread;
}
If need be you could use this as the basis as modify it to display asterisks.
Without getch to rely on and avoiding the obsolete getpass, the recommended approach is to disable terminal ECHO through termios use. After a few searches to find a canned flexible password routine, I was surprised that very few for stand-alone use with C. Rather than simply recoding getch with termios c_lflag options, slightly more generalized approach takes just a few additions. Beyond replacing getch any routine should enforce a specified maximum length to prevent overflow, truncate if the user attempt to enter beyond the maximum, and warn if truncation occurs in some manner.
Below, the additions will allow reading from any FILE * input stream, limiting the length to a specified length, provide minimal editing (backspace) ability when taking input, allow the character mask to be specified or disabled completely, and finally return the length of the password entered. A warning was added when the password entered was truncated to the maximum or specified length.
Hopefully it will prove useful to others with this question looking for a similar solution:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#define MAXPW 32
/* read a string from fp into pw masking keypress with mask char.
getpasswd will read upto sz - 1 chars into pw, null-terminating
the resulting string. On success, the number of characters in
pw are returned, -1 otherwise.
*/
ssize_t getpasswd (char **pw, size_t sz, int mask, FILE *fp)
{
if (!pw || !sz || !fp) return -1; /* validate input */
#ifdef MAXPW
if (sz > MAXPW) sz = MAXPW;
#endif
if (*pw == NULL) { /* reallocate if no address */
void *tmp = realloc (*pw, sz * sizeof **pw);
if (!tmp)
return -1;
memset (tmp, 0, sz); /* initialize memory to 0 */
*pw = (char*) tmp;
}
size_t idx = 0; /* index, number of chars in read */
int c = 0;
struct termios old_kbd_mode; /* orig keyboard settings */
struct termios new_kbd_mode;
if (tcgetattr (0, &old_kbd_mode)) { /* save orig settings */
fprintf (stderr, "%s() error: tcgetattr failed.\n", __func__);
return -1;
} /* copy old to new */
memcpy (&new_kbd_mode, &old_kbd_mode, sizeof(struct termios));
new_kbd_mode.c_lflag &= ~(ICANON | ECHO); /* new kbd flags */
new_kbd_mode.c_cc[VTIME] = 0;
new_kbd_mode.c_cc[VMIN] = 1;
if (tcsetattr (0, TCSANOW, &new_kbd_mode)) {
fprintf (stderr, "%s() error: tcsetattr failed.\n", __func__);
return -1;
}
/* read chars from fp, mask if valid char specified */
while (((c = fgetc (fp)) != '\n' && c != EOF && idx < sz - 1) ||
(idx == sz - 1 && c == 127))
{
if (c != 127) {
if (31 < mask && mask < 127) /* valid ascii char */
fputc (mask, stdout);
(*pw)[idx++] = c;
}
else if (idx > 0) { /* handle backspace (del) */
if (31 < mask && mask < 127) {
fputc (0x8, stdout);
fputc (' ', stdout);
fputc (0x8, stdout);
}
(*pw)[--idx] = 0;
}
}
(*pw)[idx] = 0; /* null-terminate */
/* reset original keyboard */
if (tcsetattr (0, TCSANOW, &old_kbd_mode)) {
fprintf (stderr, "%s() error: tcsetattr failed.\n", __func__);
return -1;
}
if (idx == sz - 1 && c != '\n') /* warn if pw truncated */
fprintf (stderr, " (%s() warning: truncated at %zu chars.)\n",
__func__, sz - 1);
return idx; /* number of chars in passwd */
}
A simple program showing the use would be as follows. If using a static array of character for holding the password, just insure a pointer is passed to the function.
int main (void ) {
char pw[MAXPW] = {0};
char *p = pw;
FILE *fp = stdin;
ssize_t nchr = 0;
printf ( "\n Enter password: ");
nchr = getpasswd (&p, MAXPW, '*', fp);
printf ("\n you entered : %s (%zu chars)\n", p, nchr);
printf ( "\n Enter password: ");
nchr = getpasswd (&p, MAXPW, 0, fp);
printf ("\n you entered : %s (%zu chars)\n\n", p, nchr);
return 0;
}
Example Output
$ ./bin/getpasswd2
Enter password: ******
you entered : 123456 (6 chars)
Enter password:
you entered : abcdef (6 chars)
The functionality of getch (which is a non-standard, Windows function) can be emulated with this code:
#include <termios.h>
#include <unistd.h>
int getch() {
struct termios oldt, newt;
int ch;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
return ch;
}
Note that your approach is not perfect - it's better to use something like ncurses or another terminal library to handle these things.
You can create your own getch() function on Linux in this manner.
int getch() {
struct termios oldtc, newtc;
int ch;
tcgetattr(STDIN_FILENO, &oldtc);
newtc = oldtc;
newtc.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newtc);
ch=getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldtc);
return ch;
}
Demo code:
int main(int argc, char **argv) {
int ch;
printf("Press x to exit.\n\n");
for (;;) {
ch = getch();
printf("ch = %c (%d)\n", ch, ch);
if(ch == 'x')
break;
}
return 0;
}
Your method is correct, however you'll need to turn off terminal echo while the password is being entered:
#include <sgtty.h>
void echo_off()
{
struct sgttyb state;
(void)ioctl(0, (int)TIOCGETP, (char *)&state);
state.sg_flags &= ~ECHO;
(void)ioctl(0, (int)TIOCSETP, (char *)&state);
}
void echo_on()
{
struct sgttyb state;
(void)ioctl(0, (int)TIOCGETP, (char *)&state);
state.sg_flags |= ECHO;
(void)ioctl(0, (int)TIOCSETP, (char *)&state);
}
Instead of getch(), why not just use getc() instead?
Thanks all of you whose help & support to solve my problem.
I find a best way to hide my password in linux that fits me best.
To use getpass() function. It just need to include "unistd.h" file.
syntex of getpass function:
char * getpass (const char *prompt)
Parameters:
prompt: string pointer to print while asking for Password
Return Value:
string pointer of password
Example:
#include <stdio.h>
#include <unistd.h>
int main()
{
char *password; // password string pointer
password = getpass("Enter Password: "); // get a password
printf("%s\n",password); // this is just for conformation
// that password stored successfully
return 1;
}
output:
Enter Password:
heet
You might use ncurses.h if it is not necessary to be portable onto Windows for that, but here is some kind of a more "portable" version:
If it is not necessery to be portable ill point you to a ncurses solution
portablegetch.h
/*portablegetch.h*/
#ifndef PGETCH
#define PGETCH
#ifdef __unix__
#include <termios.h>
#include <unistd.h>
static struct termios n_term;
static struct termios o_term;
static int
cbreak(int fd)
{
if((tcgetattr(fd, &o_term)) == -1)
return -1;
n_term = o_term;
n_term.c_lflag = n_term.c_lflag & ~(ECHO|ICANON);
n_term.c_cc[VMIN] = 1;
n_term.c_cc[VTIME]= 0;
if((tcsetattr(fd, TCSAFLUSH, &n_term)) == -1)
return -1;
return 1;
}
int
getch()
{
int cinput;
if(cbreak(STDIN_FILENO) == -1) {
fprintf(stderr, "cbreak failure, exiting \n");
exit(EXIT_FAILURE);
}
cinput = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &o_term);
return cinput;
}
#elif _MSC_VER || __WIN32__ || __MS_DOS__
#include <conio.h>
#endif
#endif
And the c-file
whatever.c
#include <stdio.h>
#include <stdlib.h>
#include "portablegetch.h"
int
main(int argc, char **argv)
{
int input;
printf("Please Enter your Password:\t");
while(( input=getch() ) != '\n')
printf("*");
printf("\n");
return EXIT_SUCCESS;
}
That should fit to your problem.
Hope that helps.
#include <termios.h>
#include <stdio.h>
static struct termios old, new;
void initTermios(int echo) {
tcgetattr(0, &old);
new = old;
new.c_lflag &= ~ICANON;
new.c_lflag &= echo ? ECHO : ~ECHO;
tcsetattr(0, TCSANOW, &new);
}
void resetTermios(void) {
tcsetattr(0, TCSANOW, &old);
}
char getch_(int echo) {
char ch;
initTermios(echo);
ch = getchar();
resetTermios();
return ch;
}
char getch(void) {
return getch_(0);
}
int main(void) {
char c;
printf("(getch example) please type a letter...");
c = getch();
printf("\nYou typed: %c\n", c);
return 0;
}
Just copy these snippet and use it. Hope it helped
Just pass for it the char* that you want to set password in and its size and the function will do its job
void set_Password(char *get_in, int sz){
for (int i = 0; i < sz;) {
char ch = getch();
if (ch == 13) {
get_in[i] = '\0';
break;
}
else if(ch != 8){
get_in[i++] = ch;
putch('*');
}
else if(i > 0)
cout << "\b \b",get_in[i--] = '\0';
}
cout << "\n";
}
This is an example, run it on your compiler
Unfortunately in the C standard library there is no such function out of the box. Maybe in third party library.
One option is use ANSI escape sequences to set the background color to foreground color in the console to conceal the password. Try this link.
With scanning the characters you can take it into a buffer. Also you need to write code if backspace is pressed, and appropriately correct the inserted password.
Here is a code which once i wrote with the curses. Compile with gcc file.c -o pass_prog -lcurses
#include <stdio.h>
#include <stdlib.h>
#include <curses.h>
#define ENOUGH_SIZE 256
#define ECHO_ON 1
#define ECHO_OFF 0
#define BACK_SPACE 127
char *my_getpass (int echo_state);
int main (void)
{
char *pass;
initscr ();
printw ("Enter Password: ");
pass = my_getpass (ECHO_ON);
printw ("\nEntered Password: %s", pass);
refresh ();
getch ();
endwin ();
return 0;
}
char *my_getpass (int echo_state)
{
char *pass, c;
int i=0;
pass = malloc (sizeof (char) * ENOUGH_SIZE);
if (pass == NULL)
{
perror ("Exit");
exit (1);
}
cbreak ();
noecho ();
while ((c=getch()) != '\n')
{
if (c == BACK_SPACE)
{
/* Do not let the buffer underflow */
if (i > 0)
{
i--;
if (echo_state == ECHO_ON)
printw ("\b \b");
}
}
else if (c == '\t')
; /* Ignore tabs */
else
{
pass[i] = c;
i = (i >= ENOUGH_SIZE) ? ENOUGH_SIZE - 1 : i+1;
if (echo_state == ECHO_ON)
printw ("*");
}
}
echo ();
nocbreak ();
/* Terminate the password string with NUL */
pass[i] = '\0';
endwin ();
return pass;
}
In C you can use getpasswd() function which pretty much doing similar thing as stty in shell, example:
#include <stdio.h>
#include <string.h>
#include <pwd.h>
#include <unistd.h>
int main()
{
char acct[80], password[80];
printf(“Account: “);
fgets(acct, 80, stdin);
acct[strlen(acct)-1] = 0; /* remove carriage return */
strncpy(password, getpass(“Password: “), 80);
printf(“You entered acct %s and pass %s\n”, acct, password);
return 0;
}
Here is equivalent shell script which use stty (which changes the settings of your tty):
save_state=$(stty -g)
/bin/echo -n “Account: “
read acct
/bin/echo -n “Password: “
stty -echo
read password # this won’t echo
stty “$save_state”
echo “”
echo account = $acct and password = $password
Source: How can I read a password without echoing it in C?
man getpass
This function is obsolete. Do not use it. If you want to read input
without terminal echoing enabled, see the description of the ECHO flag
in termios(3)
# include <termios.h>
# include <unistd.h> /* needed for STDIN_FILENO which is an int file descriptor */
struct termios tp, save;
tcgetattr( STDIN_FILENO, &tp); /* get existing terminal properties */
save = tp; /* save existing terminal properties */
tp.c_lflag &= ~ECHO; /* only cause terminal echo off */
tcsetattr( STDIN_FILENO, TCSAFLUSH, &tp ); /* set terminal settings */
/*
now input by user in terminal will not be displayed
and cursor will not move
*/
tcsetattr( STDIN_FILENO, TCSANOW, &save); /* restore original terminal settings */
If you notice, most current linux distro's do not mask a password with asterisks. Doing so divulges the length of the password which is no way beneficial. It is easier and better to simply make the cursor not move when a password is typed in. If for whatever reason you require a * to be printed for every character that's typed then you would have to grab every keypress before Enter is hit and that's always been problematic.
printf("\nENTER PASSWORD: ");
while (1)
{
ch=getch();
if(ch==13) //ON ENTER PRESS
break;
else if(ch==8) //ON BACKSPACE PRESS REMOVES CHARACTER
{
if(i>0)
{
i--;
password[i]='\0';
printf("\b \b");
}
}
else if (ch==32 || ch==9) //ON PRESSING TAB OR SPACE KEY
continue;
else
{
password[i]=ch;
i++;
printf("*");
}
}
password[i]='\0';
Here is my idea, adapted from that of the C++ official site.
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;
string getpass(const char *prompt, bool showchar = false, char echochar = '*')
{
struct termios oi, ni;
tcgetattr(STDIN_FILENO, &oi);
ni = oi;
ni.c_lflag &= ~(ICANON | ECHO);
const char DELETE = 127;
const char RETURN = 10;
string password;
unsigned char ch = 0;
cout << prompt;
tcsetattr(STDIN_FILENO, TCSANOW, &ni);
while (getchar() != RETURN) {
if (ch == DELETE) {
if(password.length != 0){
if (showchar) cout << "\b \b";
password.resize(password.length() - 1);
}
}else {
password += getchar();
if (showchar) cout << echochar;
}
}
tcsetattr(STDIN_FILENO,TCSANOW,&oi)
cout << endl;
return password;
}
It will read one character at once and add it to the string and supports showing another character.
note that the ICANON termios lflag turns off the processing carriagereturn/linefeed, and the negative ECHO termios setting turns off echo for STDIN.
when using this (with or without the echo being on) to read a password and print '*' for entered characters, it's not just a matter of reading characters until a newline/carriage return is encountered, you also have to process backspace in your 'string building routine' (else the backspaces end up in the actual string, and do not cause characters to be removed from it such as would be the case with the various string based input functions).
the same would happen in C in DOS with getch tho. that would also happily return 0x08 for backspace (or 127 or whatever your specific os uses as backspace)
keeping track of 'not deleting -before- the start of the string', replacing the 'new end of the string' with 0 and moving the current position counter back by one (unless you are at position 0) is up to the programmer with any of these functions (even the getch on dos C).
getpass() doesn't do what the user originally asked for btw, he wants *'s (which still disclose the length of the password to people standing behind him and looking at his screen, as well as in the scrollbuffer of the terminal if he doesn't close it after use). but without *'s is probably a better idea in 'non closed environments'.
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.
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:
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++.
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.
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).
I found this on another forum while looking to solve the same problem. I've modified it a bit from what I found. It works great. I'm running OS X, so if you're running Microsoft, you'll need to find the correct system() command to switch to raw and cooked modes.
#include <iostream>
#include <stdio.h>
using namespace std;
int main() {
// Output prompt
cout << "Press any key to continue..." << endl;
// Set terminal to raw mode
system("stty raw");
// Wait for single character
char input = getchar();
// Echo input:
cout << "--" << input << "--";
// Reset terminal to normal "cooked" mode
system("stty cooked");
// And we're out of here
return 0;
}
If you are on windows, you can use PeekConsoleInput to detect if there's any input,
HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
DWORD events;
INPUT_RECORD buffer;
PeekConsoleInput( handle, &buffer, 1, &events );
then use ReadConsoleInput to "consume" the input character ..
PeekConsoleInput(handle, &buffer, 1, &events);
if(events > 0)
{
ReadConsoleInput(handle, &buffer, 1, &events);
return buffer.Event.KeyEvent.wVirtualKeyCode;
}
else return 0
to be honest this is from some old code I have, so you have to fiddle a bit with it.
The cool thing though is that it reads input without prompting for anything, so the characters are not displayed at all.
CONIO.H
the functions you need are:
int getch();
Prototype
int _getch(void);
Description
_getch obtains a character from stdin. Input is unbuffered, and this
routine will return as soon as a character is available without
waiting for a carriage return. The character is not echoed to stdout.
_getch bypasses the normal buffering done by getchar and getc. ungetc
cannot be used with _getch.
Synonym
Function: getch
int kbhit();
Description
Checks if a keyboard key has been pressed but not yet read.
Return Value
Returns a non-zero value if a key was pressed. Otherwise, returns 0.
libconio
http://sourceforge.net/projects/libconio
or
Linux c++ implementation of conio.h
http://sourceforge.net/projects/linux-conioh
#include <conio.h>
if (kbhit() != 0) {
cout << getch() << endl;
}
This uses kbhit() to check if the keyboard is being pressed and uses getch() to get the character that is being pressed.
I use kbhit() to see if a char is present and then getchar() to read the data.
On windows, you can use "conio.h". On linux, you will have to implement your own kbhit().
See code below:
// kbhit
#include <stdio.h>
#include <sys/ioctl.h> // For FIONREAD
#include <termios.h>
#include <stdbool.h>
int kbhit(void) {
static bool initflag = false;
static const int STDIN = 0;
if (!initflag) {
// Use termios to turn off line buffering
struct termios term;
tcgetattr(STDIN, &term);
term.c_lflag &= ~ICANON;
tcsetattr(STDIN, TCSANOW, &term);
setbuf(stdin, NULL);
initflag = true;
}
int nbbytes;
ioctl(STDIN, FIONREAD, &nbbytes); // 0 is STDIN
return nbbytes;
}
// main
#include <unistd.h>
int main(int argc, char** argv) {
char c;
//setbuf(stdout, NULL); // Optional: No buffering.
//setbuf(stdin, NULL); // Optional: No buffering.
printf("Press key");
while (!kbhit()) {
printf(".");
fflush(stdout);
sleep(1);
}
c = getchar();
printf("\nChar received:%c\n", c);
printf("Done.\n");
return 0;
}
ncurses provides a nice way to do this!
Also this is my very first post (that I can remember), so any comments at all are welcome. I will appreciate useful ones, but all are welcome!
to compile: g++ -std=c++11 -pthread -lncurses .cpp -o
#include <iostream>
#include <ncurses.h>
#include <future>
char get_keyboard_input();
int main(int argc, char *argv[])
{
initscr();
raw();
noecho();
keypad(stdscr,true);
auto f = std::async(std::launch::async, get_keyboard_input);
while (f.wait_for(std::chrono::milliseconds(20)) != std::future_status::ready)
{
// do some work
}
endwin();
std::cout << "returned: " << f.get() << std::endl;
return 0;
}
char get_keyboard_input()
{
char input = '0';
while(input != 'q')
{
input = getch();
}
return input;
}
Since previous solutions here don't work cross-platform and have trouble with special keys, here is my solution that works on both Windows and Linux and uses a minimum of external libraries (Windows.h for Windows and sys/ioctl.h+termios.h for Linux).
For ASCII characters (newline/tab/space/backspace/delete, !"#$%&'()*+,-./0-9:;<=>?#A-Z[]^_`a-z{|}~üäÄöÖÜßµ´§°¹³²), the ASCII codes (positive numbers) are returned and for special keys (arrow keys, page up/down, pos1/end, escape, insert, F1-F12), the negative of Windows Virtual-Key codes (negative numbers) are returned.
#include <iostream>
#include <string>
#include <thread> // contains <chrono>
using namespace std;
void println(const string& s="") {
cout << s << endl;
}
void sleep(const double t) {
if(t>0.0) this_thread::sleep_for(chrono::milliseconds((int)(1E3*t+0.5)));
}
// ASCII codes (key>0): 8 backspace, 9 tab, 10 newline, 27 escape, 127 delete, !"#$%&'()*+,-./0-9:;<=>?#A-Z[]^_`a-z{|}~üäÄöÖÜßµ´§°¹³²
// control key codes (key<0): -38/-40/-37/-39 up/down/left/right arrow, -33/-34 page up/down, -36/-35 pos1/end
// other key codes (key<0): -45 insert, -144 num lock, -20 caps lock, -91 windows key, -93 kontext menu key, -112 to -123 F1 to F12
// not working: ¹ (251), num lock (-144), caps lock (-20), windows key (-91), kontext menu key (-93), F11 (-122)
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#include <Windows.h>
int key_press() { // not working: F11 (-122, toggles fullscreen)
KEY_EVENT_RECORD keyevent;
INPUT_RECORD irec;
DWORD events;
while(true) {
ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &irec, 1, &events);
if(irec.EventType==KEY_EVENT&&((KEY_EVENT_RECORD&)irec.Event).bKeyDown) {
keyevent = (KEY_EVENT_RECORD&)irec.Event;
const int ca = (int)keyevent.uChar.AsciiChar;
const int cv = (int)keyevent.wVirtualKeyCode;
const int key = ca==0 ? -cv : ca+(ca>0?0:256);
switch(key) {
case -16: continue; // disable Shift
case -17: continue; // disable Ctrl / AltGr
case -18: continue; // disable Alt / AltGr
case -220: continue; // disable first detection of "^" key (not "^" symbol)
case -221: continue; // disable first detection of "`" key (not "`" symbol)
case -191: continue; // disable AltGr + "#"
case -52: continue; // disable AltGr + "4"
case -53: continue; // disable AltGr + "5"
case -54: continue; // disable AltGr + "6"
case -12: continue; // disable num block 5 with num lock deactivated
case 13: return 10; // enter
case -46: return 127; // delete
case -49: return 251; // ¹
case 0: continue;
case 1: continue; // disable Ctrl + a (selects all text)
case 2: continue; // disable Ctrl + b
case 3: continue; // disable Ctrl + c (terminates program)
case 4: continue; // disable Ctrl + d
case 5: continue; // disable Ctrl + e
case 6: continue; // disable Ctrl + f (opens search)
case 7: continue; // disable Ctrl + g
//case 8: continue; // disable Ctrl + h (ascii for backspace)
//case 9: continue; // disable Ctrl + i (ascii for tab)
case 10: continue; // disable Ctrl + j
case 11: continue; // disable Ctrl + k
case 12: continue; // disable Ctrl + l
//case 13: continue; // disable Ctrl + m (breaks console, ascii for new line)
case 14: continue; // disable Ctrl + n
case 15: continue; // disable Ctrl + o
case 16: continue; // disable Ctrl + p
case 17: continue; // disable Ctrl + q
case 18: continue; // disable Ctrl + r
case 19: continue; // disable Ctrl + s
case 20: continue; // disable Ctrl + t
case 21: continue; // disable Ctrl + u
case 22: continue; // disable Ctrl + v (inserts clipboard)
case 23: continue; // disable Ctrl + w
case 24: continue; // disable Ctrl + x
case 25: continue; // disable Ctrl + y
case 26: continue; // disable Ctrl + z
default: return key; // any other ASCII/virtual character
}
}
}
}
#elif defined(__linux__)
#include <sys/ioctl.h>
#include <termios.h>
int key_press() { // not working: ¹ (251), num lock (-144), caps lock (-20), windows key (-91), kontext menu key (-93)
struct termios term;
tcgetattr(0, &term);
while(true) {
term.c_lflag &= ~(ICANON|ECHO); // turn off line buffering and echoing
tcsetattr(0, TCSANOW, &term);
int nbbytes;
ioctl(0, FIONREAD, &nbbytes); // 0 is STDIN
while(!nbbytes) {
sleep(0.01);
fflush(stdout);
ioctl(0, FIONREAD, &nbbytes); // 0 is STDIN
}
int key = (int)getchar();
if(key==27||key==194||key==195) { // escape, 194/195 is escape for °ß´äöüÄÖÜ
key = (int)getchar();
if(key==91) { // [ following escape
key = (int)getchar(); // get code of next char after \e[
if(key==49) { // F5-F8
key = 62+(int)getchar(); // 53, 55-57
if(key==115) key++; // F5 code is too low by 1
getchar(); // take in following ~ (126), but discard code
} else if(key==50) { // insert or F9-F12
key = (int)getchar();
if(key==126) { // insert
key = 45;
} else { // F9-F12
key += 71; // 48, 49, 51, 52
if(key<121) key++; // F11 and F12 are too low by 1
getchar(); // take in following ~ (126), but discard code
}
} else if(key==51||key==53||key==54) { // delete, page up/down
getchar(); // take in following ~ (126), but discard code
}
} else if(key==79) { // F1-F4
key = 32+(int)getchar(); // 80-83
}
key = -key; // use negative numbers for escaped keys
}
term.c_lflag |= (ICANON|ECHO); // turn on line buffering and echoing
tcsetattr(0, TCSANOW, &term);
switch(key) {
case 127: return 8; // backspace
case -27: return 27; // escape
case -51: return 127; // delete
case -164: return 132; // ä
case -182: return 148; // ö
case -188: return 129; // ü
case -132: return 142; // Ä
case -150: return 153; // Ö
case -156: return 154; // Ü
case -159: return 225; // ß
case -181: return 230; // µ
case -167: return 245; // §
case -176: return 248; // °
case -178: return 253; // ²
case -179: return 252; // ³
case -180: return 239; // ´
case -65: return -38; // up arrow
case -66: return -40; // down arrow
case -68: return -37; // left arrow
case -67: return -39; // right arrow
case -53: return -33; // page up
case -54: return -34; // page down
case -72: return -36; // pos1
case -70: return -35; // end
case 0: continue;
case 1: continue; // disable Ctrl + a
case 2: continue; // disable Ctrl + b
case 3: continue; // disable Ctrl + c (terminates program)
case 4: continue; // disable Ctrl + d
case 5: continue; // disable Ctrl + e
case 6: continue; // disable Ctrl + f
case 7: continue; // disable Ctrl + g
case 8: continue; // disable Ctrl + h
//case 9: continue; // disable Ctrl + i (ascii for tab)
//case 10: continue; // disable Ctrl + j (ascii for new line)
case 11: continue; // disable Ctrl + k
case 12: continue; // disable Ctrl + l
case 13: continue; // disable Ctrl + m
case 14: continue; // disable Ctrl + n
case 15: continue; // disable Ctrl + o
case 16: continue; // disable Ctrl + p
case 17: continue; // disable Ctrl + q
case 18: continue; // disable Ctrl + r
case 19: continue; // disable Ctrl + s
case 20: continue; // disable Ctrl + t
case 21: continue; // disable Ctrl + u
case 22: continue; // disable Ctrl + v
case 23: continue; // disable Ctrl + w
case 24: continue; // disable Ctrl + x
case 25: continue; // disable Ctrl + y
case 26: continue; // disable Ctrl + z (terminates program)
default: return key; // any other ASCII character
}
}
}
#endif // Windows/Linux
Finally, here is an example on how to use it:
int main() {
while(true) {
const int key = key_press(); // blocks until a key is pressed
println("Input is: "+to_string(key)+", \""+(char)key+"\"");
}
return 0;
}
Assuming Windows, take a look at the ReadConsoleInput function.
C and C++ take a very abstract view of I/O, and there is no standard way of doing what you want. There are standard ways to get characters from the standard input stream, if there are any to get, and nothing else is defined by either language. Any answer will therefore have to be platform-specific, perhaps depending not only on the operating system but also the software framework.
There's some reasonable guesses here, but there's no way to answer your question without knowing what your target environment is.
The closest thing to portable is to use the ncurses library to put the terminal into "cbreak mode". The API is gigantic; the routines you'll want most are
initscr and endwin
cbreak and nocbreak
getch
Good luck!
I always wanted a loop to read my input without pressing return key.
this worked for me.
#include<stdio.h>
main()
{
char ch;
system("stty raw");//seting the terminal in raw mode
while(1)
{
ch=getchar();
if(ch=='~'){ //terminate or come out of raw mode on "~" pressed
system("stty cooked");
//while(1);//you may still run the code
exit(0); //or terminate
}
printf("you pressed %c\n ",ch); //write rest code here
}
}
works for me on windows:
#include <conio.h>
char c = _getch();
The following is a solution extracted from Expert C Programming: Deep Secrets, which is supposed to work on SVr4. It uses stty and ioctl.
#include <sys/filio.h>
int kbhit()
{
int i;
ioctl(0, FIONREAD, &i);
return i; /* return a count of chars available to read */
}
main()
{
int i = 0;
intc='';
system("stty raw -echo");
printf("enter 'q' to quit \n");
for (;c!='q';i++) {
if (kbhit()) {
c=getchar();
printf("\n got %c, on iteration %d",c, i);
}
}
system("stty cooked echo");
}
You can do it portably using SDL (the Simple DirectMedia Library), though I suspect you may not like its behavior. When I tried it, I had to have SDL create a new video window (even though I didn't need it for my program) and have this window "grab" almost all keyboard and mouse input (which was okay for my usage but could be annoying or unworkable in other situations). I suspect it's overkill and not worth it unless complete portability is a must--otherwise try one of the other suggested solutions.
By the way, this will give you key press and release events separately, if you're into that.
Variant of ssinfod's answer for Linux that's a little cleaner for my tastes, implemented for wcout and wchar_t, and erases invalid characters without bugs.
#include <functional>
//For Linux kbhit(). For Windows, use conio.h.
#ifdef __unix__
#include <sys/ioctl.h> //For FIONREAD.
#include <termios.h>
//Call this at program start to setup for kbhit.
void initTerminalInput()
{
//Disable internal buffering.
std::wcout << std::unitbuf;
//Turn off line buffering.
struct termios term;
tcgetattr(0, &term);
term.c_lflag &= ~ICANON;
tcsetattr(0, TCSANOW, &term);
setbuf(stdin, NULL);
}
//Returns 0 if there's no input character to read.
int kbhit()
{
static int nbbytes;
ioctl(0, FIONREAD, &nbbytes);
return nbbytes;
}
#endif
//Waits for and retrieves a single validated character, calling a validation function on each character entered and
//erasing any that are invalid (when the validation function returns false).
static wchar_t getWChar(std::function<bool(wchar_t)> validationFunction)
{
static wchar_t inputWChar;
do
{
//Wait until there's an input character.
while (!kbhit())
{
}
inputWChar = getwchar();
//Validate the input character.
if (validationFunction(inputWChar))
{
//Valid.
break;
}
else
{
//Erase the invalid character.
std::wcout << L"\b \b";
}
} while (true);
return inputWChar;
}
In the below example, I wanted for the user to enter either 1, 2, or 3. Any other characters entered will not be displayed, and it will wait until one of the valid characters is pressed:
int main()
{
#ifdef __unix__
initTerminalInput();
#endif
getWChar([] (wchar_t inputWChar)
{
return (inputWChar >= L'1' && inputWChar <= L'3');
});
return 0;
}
Here's a version that doesn't shell out to the system (written and tested on macOS 10.14)
#include <unistd.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
char* getStr( char* buffer , int maxRead ) {
int numRead = 0;
char ch;
struct termios old = {0};
struct termios new = {0};
if( tcgetattr( 0 , &old ) < 0 ) perror( "tcgetattr() old settings" );
if( tcgetattr( 0 , &new ) < 0 ) perror( "tcgetaart() new settings" );
cfmakeraw( &new );
if( tcsetattr( 0 , TCSADRAIN , &new ) < 0 ) perror( "tcssetattr makeraw new" );
for( int i = 0 ; i < maxRead ; i++) {
ch = getchar();
switch( ch ) {
case EOF:
case '\n':
case '\r':
goto exit_getStr;
break;
default:
printf( "%1c" , ch );
buffer[ numRead++ ] = ch;
if( numRead >= maxRead ) {
goto exit_getStr;
}
break;
}
}
exit_getStr:
if( tcsetattr( 0 , TCSADRAIN , &old) < 0) perror ("tcsetattr reset to old" );
printf( "\n" );
return buffer;
}
int main( void )
{
const int maxChars = 20;
char stringBuffer[ maxChars+1 ];
memset( stringBuffer , 0 , maxChars+1 ); // initialize to 0
printf( "enter a string: ");
getStr( stringBuffer , maxChars );
printf( "you entered: [%s]\n" , stringBuffer );
}
ON WINDOWS I do so:
#include <Windows.h>
int main()
{
system("PAUSE>NUL");
if (GetKeyState(0x41/*(the 'A' key, choosen e.g.)*/) & (0xff80/*That stands for "Default state / Key Down"*/)) {
//whatever I want to do after 'A' is pressed
}
}
Here can be found a list of keys with its own hex value for each:
https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
I was running into this same issue. Here is a small solution for windows console using cygwin g++ with if(GetKeyState(keycode) & bitANDcompare){};.
#include <windows.h>
#include <fstream>
#include <iostream>
using namespace std;
void clear() {
COORD topLeft = { 0, 0 };
HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO screen;
DWORD written;
GetConsoleScreenBufferInfo(console, &screen);
FillConsoleOutputCharacterA(
console, ' ', screen.dwSize.X * screen.dwSize.Y, topLeft, &written
);
FillConsoleOutputAttribute(
console, FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE,
screen.dwSize.X * screen.dwSize.Y, topLeft, &written
);
SetConsoleCursorPosition(console, topLeft);
}
class Keyclick{
private:
int key;
char id;
public:
bool keydown = false;
Keyclick(int key1, char id1){
key=key1;
id=id1;
};
void watch(){
if(keydown==false){
if(GetKeyState(key) & 0x8000 ){
cout << id;
cout << " pressed.\r\n";
keydown = true;
}
}
if(keydown == true){
if(!(GetKeyState(key) & 0x8000)) {
cout << "released!!!!!!!!!!\r\n";
keydown = false;
clear();
}
}
};
};
int main()
{
bool primaryloop =true;
Keyclick keysp(VK_SPACE,'S');
Keyclick keyw(0x57,'w');
Keyclick keya(0x41,'a');
Keyclick keys(0x53,'s');
Keyclick keyd(0x44,'d');
Keyclick keyesc(VK_ESCAPE,'E');
while(primaryloop){
keysp.watch();
keyw.watch();
keya.watch();
keys.watch();
keyd.watch();
keyesc.watch();
if(keyesc.keydown){
primaryloop=false;
};
}
return 0;
}
https://github.com/wark77/windows_console_keypoller/blob/main/getkeystate_SOLUTION01.cpp
Pipe demo: how to read keyboard presses from a system call pipe in C
Also ideally it wouldn't echo the input character to the screen. I just want to capture keystrokes with out effecting the console screen.
To do this on Linux you can use the following bash command:
read -sn1 c && printf "You Pressed: %s\n" "$c"
See my answer here for details on this: shell script respond to keypress.
So, to do this in C or C++ on Linux, you simply need to call the above bash command via a system call through a pipe with popen() and fgets() so you can read the output from the bash command.
Here is a full example which runs fine on Linux in both C and C++:
read_system_call_via_pipe__keypress.c:
#include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
#include <stdint.h> // For `uint8_t`, `int8_t`, etc.
#include <stdio.h> // For `printf()`
#include <stdlib.h>
#define BUFSIZE 32
// Read a keyboard key press and return the character pressed, or a negative
// number in the event of an error.
// NB: for help reading output from system calls, see here:
// 1. https://stackoverflow.com/a/28971647/4561887
// 2. https://stackoverflow.com/a/18297075/4561887
char getKeypress()
{
// This bash cmd is from my answer here:
// https://stackoverflow.com/a/70979348/4561887
const char* cmd = "bash -c 'read -s -n1 c && printf \"%s\" \"$c\"'";
FILE *fp = popen(cmd, "r");
if (fp == NULL)
{
printf("\nError opening pipe!\n");
return -1;
}
char buf[BUFSIZE] = {0};
char* retval1 = fgets(buf, BUFSIZE, fp);
if (retval1 == NULL)
{
printf("\nFailed to read cmd response.\n");
return -2;
}
// See meaning of this return value here:
// https://stackoverflow.com/questions/43116/how-can-i-run-an-external-program-from-c-and-parse-its-output/28971647#comment60311936_28971647
int retval2 = pclose(fp);
if (retval2 == -1)
{
printf("\nError obtaining the cmd's exit status code.\n");
return -3;
}
else if (retval2 != 0)
{
printf("\nCommand exited with exit status code %i.\n", retval2);
return -4;
}
char keyPressed = buf[0];
return keyPressed;
}
// int main(int argc, char *argv[]) // alternative prototype
int main()
{
printf("Press any key to continue: ");
fflush(stdout);
char keyPressed = getKeypress();
if (keyPressed > 0)
{
printf("\nKey pressed = %c\n", keyPressed);
}
return 0;
}
The C and C++ compile and run commands are part of the output below. Here are some demos:
In C:
eRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=gnu17 read_keypress_system_call.c -o bin/a && bin/a
Press any key to continue:
Key pressed = P
OR, in C++:
eRCaGuy_hello_world/c$ g++ -Wall -Wextra -Werror -O3 -std=c++17 read_keypress_system_call.c -o bin/a && bin/a
Press any key to continue:
Key pressed = u
See also:
To take this one step further, see my other answer here where I detect and parse 3 chars at a time in order to detect arrow key presses Up, Down, Left, or Right: Read Key pressings in C ex. Arrow keys, Enter key
References:
How I learned to read from a pipe to get system call output:
How can I run an external program from C and parse its output?
Is there a way to obtain the output of a linux command(like ifconfig) on a .txt file using a C program? [duplicate]
How to compile and use popen() in C: use -std=gnu17 instead of -std=c17: popen implicitly declared even though #include <stdio.h> is added
[my answer] How to read without blocking, via bash: shell script respond to keypress
[my answer] How do I read in the Enter key as an input in C?
All 3 Q&As on this topic
Capture characters from standard input without waiting for enter to be pressed
C non-blocking keyboard input
How to avoid pressing Enter with getchar() for reading a single character only?