'Read Key presses in C; ex.: Arrow keys, Enter key
I know how to use events to test when a key is pressed or not, but in C I never found out how to do that.
What I want exactly is a "KeyListener" that listens for the Up, Down, Left and Right arrow keys. I need it to work in Linux, so no Windows libraries. And, if possible, using no 3rd party libraries is the best option for me.
Pseudocode of what I want:
int main() {
// key listener {
// if(key == up) { // do something }
// if(key == down) { // do something }
// if(key == left) { // do something }
// if(key == right) { // do something }
// }
}
Solution 1:[1]
How about using SDL to read the keyboard too.
SDL_Event event;
.
.
/* Poll for events. SDL_PollEvent() returns 0 when there are no */
/* more events on the event queue, our while loop will exit when */
/* that occurs. */
while( SDL_PollEvent( &event ) ){
/* We are only worried about SDL_KEYDOWN and SDL_KEYUP events */
switch( event.type ){
case SDL_KEYDOWN:
printf( "Key press detected\n" );
if (event.key.keysym.sym==SDLK_UP)
printf( "It was the UP key\n" );
break;
case SDL_KEYUP:
printf( "Key release detected\n" );
break;
default:
break;
}
}
.
.
Source: http://www.libsdl.org/docs/html/guideinputkeyboard.html
Solution 2:[2]
Here's a hack based on my other answer here: Capture characters from standard input without waiting for enter to be pressed. I use a bash system call in C or C++ to read and parse arrow key presses. Arrow key presses come in as 3 chars, so I parse 3 chars at a time and figure out which arrow key it was.
There is no need to ever press Enter (which is unlike when using the getc()
command, which will only return when you press Enter). The arrow key presses are detected immediately after you press them.
read_system_call_via_pipe__arrow_keypresses.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
typedef enum arrowKey_e
{
ARROWKEY_UNKNOWN = 0,
ARROWKEY_UP,
ARROWKEY_DOWN,
ARROWKEY_LEFT,
ARROWKEY_RIGHT,
} arrowKey_t;
// Modeled after my answer here: https://stackoverflow.com/a/54728664/4561887
const char* arrowKeyGetName(arrowKey_t arrowKey)
{
const char* arrowKeyName = "TBD";
switch (arrowKey)
{
case ARROWKEY_UNKNOWN:
arrowKeyName = "ARROWKEY_UNKNOWN";
break;
case ARROWKEY_UP:
arrowKeyName = "ARROWKEY_UP";
break;
case ARROWKEY_DOWN:
arrowKeyName = "ARROWKEY_DOWN";
break;
case ARROWKEY_LEFT:
arrowKeyName = "ARROWKEY_LEFT";
break;
case ARROWKEY_RIGHT:
arrowKeyName = "ARROWKEY_RIGHT";
break;
}
return arrowKeyName;
}
// 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
arrowKey_t readArrowKeyPress()
{
arrowKey_t arrowKeyPressed = ARROWKEY_UNKNOWN;
// This bash cmd is from my answer here:
// https://stackoverflow.com/a/70979348/4561887
// `-n3` here means to read 3 chars at once, since an arrow key press
// comes in as 3 chars.
// The `-t .01` forces a timeout of that many seconds, which means the loop
// interval will be this fast.
const char* cmd = "bash -c 'read -s -t .1 -n3 c && printf \"%s\" \"$c\"'";
FILE *fp = popen(cmd, "r");
if (fp == NULL)
{
printf("\nError opening pipe!\n");
return arrowKeyPressed;
}
char buf[BUFSIZE] = {0};
char* retval1 = fgets(buf, BUFSIZE, fp);
if (retval1 == NULL)
{
// printf("\nFailed to read cmd response.\n");
// Timeout occured; just exit.
return arrowKeyPressed;
}
// 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 arrowKeyPressed;
}
else if (retval2 != 0)
{
printf("\nCommand exited with exit status code %i.\n", retval2);
return arrowKeyPressed;
}
// Map the readings to arrow keys
if ((buf[0] == 27) && (buf[1] == 91) && (buf[2] == 65))
{
arrowKeyPressed = ARROWKEY_UP;
}
else if ((buf[0] == 27) && (buf[1] == 91) && (buf[2] == 66))
{
arrowKeyPressed = ARROWKEY_DOWN;
}
else if ((buf[0] == 27) && (buf[1] == 91) && (buf[2] == 67))
{
arrowKeyPressed = ARROWKEY_RIGHT;
}
else if ((buf[0] == 27) && (buf[1] == 91) && (buf[2] == 68))
{
arrowKeyPressed = ARROWKEY_LEFT;
}
return arrowKeyPressed;
}
// int main(int argc, char *argv[]) // alternative prototype
int main()
{
printf("Press any arrow key. Press Ctrl + C to quit.\n");
fflush(stdout);
while (true)
{
arrowKey_t arrowKeyPressed = readArrowKeyPress();
if (arrowKeyPressed == ARROWKEY_UNKNOWN)
{
continue;
}
printf("Key pressed = %s\n", arrowKeyGetName(arrowKeyPressed));
}
return 0;
}
Sample build and run output:
eRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=gnu17 read_arrow_keypresses_system_call.c -o bin/a && bin/a
Press any arrow key. Press Ctrl + C to quit.
Key pressed = ARROWKEY_DOWN
Key pressed = ARROWKEY_UP
Key pressed = ARROWKEY_LEFT
Key pressed = ARROWKEY_RIGHT
Key pressed = ARROWKEY_UP
Key pressed = ARROWKEY_DOWN
Key pressed = ARROWKEY_LEFT
Key pressed = ARROWKEY_RIGHT
Key pressed = ARROWKEY_UP
Key pressed = ARROWKEY_DOWN
Key pressed = ARROWKEY_LEFT
Key pressed = ARROWKEY_RIGHT
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 |