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://mcmap.net/q/17738/-stm32-how-to-get-last-reset-status
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://mcmap.net/q/65640/-how-can-i-run-an-external-program-from-c-and-parse-its-output
// 2. https://mcmap.net/q/66518/-is-there-a-way-to-obtain-the-output-of-a-linux-command-like-ifconfig-on-a-txt-file-using-a-c-program-duplicate
arrowKey_t readArrowKeyPress()
{
arrowKey_t arrowKeyPressed = ARROWKEY_UNKNOWN;
// This bash cmd is from my answer here:
// https://mcmap.net/q/119459/-shell-script-respond-to-keypress
// `-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://mcmap.net/q/65640/-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