User Input in the Terminal

Handling Arrow Key Input in C++

Is it possible to get arrow key input in C++ for navigation purposes?

Abstract art representing computer programming

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.

This Question is from the Lesson:

User Input in the Terminal

This lesson introduces the fundamentals of capturing user input, using std::cin and std::getline

Answers to questions are automatically generated and may not have been reviewed.

This Question is from the Lesson:

User Input in the Terminal

This lesson introduces the fundamentals of capturing user input, using std::cin and std::getline

3D art showing a progammer setting up a development environment
Part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, unlimited access

This course includes:

  • 57 Lessons
  • Over 200 Quiz Questions
  • 95% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Free, Unlimited Access

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Screenshot from Warhammer: Total War
Screenshot from Tomb Raider
Screenshot from Jedi: Fallen Order
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved