Handling Arrow Key Input in C++
Is it possible to get arrow key input in C++ for navigation purposes?
Yes, it is possible to handle arrow key input in C++ for navigation purposes, but it requires some platform-specific code.
Arrow keys typically generate special key codes that we need to capture and interpret. Let's explore how to do this on both Windows and Unix-like systems.
Windows Implementation
On Windows, we can use the _getch()
function from <conio.h>
to capture arrow key input:
#include <iostream>
#include <conio.h> // Windows-specific header
int main(){
std::cout <<
"Use arrow keys to navigate:\n";
while (true) {
int dir = _getch();
// Special key indicator
if (dir == 0 || dir == 224) {
switch (_getch()) {
case 72:
std::cout << "Up ";
break;
case 80:
std::cout << "Down ";
break;
case 75:
std::cout << "Left ";
break;
case 77:
std::cout << "Right ";
break;
}
} else { break; }
}
}
Up Down Left Right
This Windows implementation uses _getch()
to read key presses without echoing them to the console. Arrow keys generate two characters: a special key indicator (0 or 224) followed by the actual arrow key code.
Unix-like Systems Implementation
For Unix-like systems, we can use the termios.h
header to put the terminal in raw mode and capture individual key presses:
#include <iostream>
#include <termios.h>
#include <unistd.h>
enum class Direction {
Up,
Down,
Left,
Right,
Unknown
};
termios orig_termios;
void disableRawMode(){
tcsetattr(STDIN_FILENO, TCSAFLUSH,
&orig_termios);
}
void enableRawMode(){
tcgetattr(STDIN_FILENO, &orig_termios);
atexit(disableRawMode);
termios raw = orig_termios;
raw.c_lflag &= ~(ECHO | ICANON);
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}
Direction getArrowKeyInput(){
char c;
if (read(STDIN_FILENO, &c, 1) == 1) {
if (c == '\x1b') {
char seq[3];
if (read(STDIN_FILENO, &seq[0], 1) !=
1) return Direction::Unknown;
if (read(STDIN_FILENO, &seq[1], 1) !=
1) return Direction::Unknown;
if (seq[0] == '[') {
switch (seq[1]) {
case 'A':
return Direction::Up;
case 'B':
return Direction::Down;
case 'C':
return Direction::Right;
case 'D':
return Direction::Left;
}
}
}
}
return Direction::Unknown;
}
int main(){
enableRawMode();
std::cout <<
"Use arrow keys to navigate (press 'q' to "
"quit):\n";
char c;
while (read(STDIN_FILENO, &c, 1) == 1 && c !=
'q') {
Direction dir = getArrowKeyInput();
switch (dir) {
case Direction::Up:
std::cout << "Up ";
break;
case Direction::Down:
std::cout << "Down ";
break;
case Direction::Left:
std::cout << "Left ";
break;
case Direction::Right:
std::cout << "Right ";
break;
default:
break;
}
}
}
Up Down Left Right
This Unix-like implementation puts the terminal in raw mode to read individual key presses. Arrow keys generate a sequence of characters starting with the escape character ('\x1b').
Cross-platform Solution
To create a cross-platform solution, you can use conditional compilation:
#include <iostream>
#ifdef _WIN32
#include <conio.h>
#else
#include <termios.h>
#include <unistd.h>
#endif
enum class Direction {
Up,
Down,
Left,
Right,
Unknown
};
#ifndef _WIN32
termios orig_termios;
void disableRawMode() {
tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
}
void enableRawMode() {
tcgetattr(STDIN_FILENO, &orig_termios);
atexit(disableRawMode);
termios raw = orig_termios;
raw.c_lflag &= ~(ECHO | ICANON);
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}
#endif
Direction getArrowKeyInput(){
#ifdef _WIN32
switch (_getch()) {
case 72:
return Direction::Up;
case 80:
return Direction::Down;
case 75:
return Direction::Left;
case 77:
return Direction::Right;
default:
return Direction::Unknown;
}
#else
char c;
if (read(STDIN_FILENO, &c, 1) == 1) {
if (c == '\x1b') {
char seq[3];
if (read(STDIN_FILENO, &seq[0], 1) != 1)
return Direction::Unknown;
if (read(STDIN_FILENO, &seq[1], 1) != 1)
return Direction::Unknown;
if (seq[0] == '[') {
switch (seq[1]) {
case 'A':
return Direction::Up;
case 'B':
return Direction::Down;
case 'C':
return Direction::Right;
case 'D':
return Direction::Left;
}
}
}
}
#endif
return Direction::Unknown;
}
int main(){
#ifndef _WIN32
enableRawMode();
#endif
std::cout <<
"Use arrow keys to navigate (press 'q' to "
"quit):\n";
int ch;
while (true) {
#ifdef _WIN32
ch = _getch();
#else
read(STDIN_FILENO, &ch, 1);
#endif
if (ch == 'q') break;
if (ch != 0 && ch != 224) continue;
Direction dir = getArrowKeyInput();
switch (dir) {
case Direction::Up:
std::cout << "Up\n";
break;
case Direction::Down:
std::cout << "Down\n";
break;
case Direction::Left:
std::cout << "Left\n";
break;
case Direction::Right:
std::cout << "Right\n";
break;
default:
break;
}
}
}
Up Down Left Right
This cross-platform solution uses conditional compilation to choose the appropriate implementation based on the target platform. It provides a consistent interface for handling arrow key input across different operating systems.
Remember that this low-level input handling can make your program less portable and more complex. For more advanced applications, consider using cross-platform libraries like ncurses or SDL that provide higher-level abstractions for keyboard input and terminal control.
User Input in the Terminal
This lesson introduces the fundamentals of capturing user input, using std::cin
and std::getline()