Looking to build a terminal based game. I am assuming X-Term like terminal. This means: * I can use the X-Term control codes to clear and move around the screen. * I can use `tcsetattr` functions to make standard input none buffered (I think). I know this game loop runs as basically a busy wait. I will sove that at the `select()` command by adding the appropriate timeout there (I just need to calculate an appropriate value). Note: I installed the signal handlers so that when the application is being forced to quit I can still restore the terminal to its original state before the application quite. You will notice the `std::cout << showCursor;` to turn the cursor back on and the destructor sets the terminal state back to its original value: `tcsetattr(STDIN_FILENO, TCSAFLUSH, &originalConfig);` This is the shell for my game (I have not implemented game logic). I wanted to make sure I am not messing stuff up around display and terminal manipulation before I go any further. #include <iostream> #include <signal.h> #include <termios.h> #include <stdio.h> #include <unistd.h> static volatile sig_atomic_t done = 0; extern "C" void handle_done(int signum) { if (!done) { done = signum; } } class Game { static constexpr int width = 40; static constexpr int height = 20; static constexpr char clearScreen[] = "\033[2J"; static constexpr char movetoZeroZero[] = "\033[0;0H"; static constexpr char hideCursor[] = "\033[?25l"; static constexpr char showCursor[] = "\033[?25h"; termios originalConfig; bool gameOver; char getChar(int x, int y) { if (y == 0 || y == height -1 || x == 0 || x == width -1 ) { return '#'; } return ' '; } void draw(bool dirty) { std::cout << movetoZeroZero << "Snake V1\n"; for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { std::cout << getChar(x, y); } std::cout << "\n"; } std::cout << std::flush; } bool input() { if (done) { gameOver = true; } fd_set input; FD_ZERO(&input); FD_SET(STDIN_FILENO, &input); timeval timeout{0, 0}; if (select(STDIN_FILENO+1, &input, nullptr, nullptr, &timeout) > 0) { char key = std::cin.get(); if (key == 'Q') { gameOver = true; } } return false; } bool logic() { return false; } int install_done(const int signum) { struct sigaction act {0}; memset(&act, 0, sizeof act); sigemptyset(&act.sa_mask); act.sa_handler = handle_done; act.sa_flags = 0; if (sigaction(signum, &act, NULL) == -1) { return errno; } return 0; } public: Game() : gameOver(false) { if (install_done(SIGINT) || install_done(SIGTERM) || install_done(SIGHUP)) { throw std::runtime_error("Fail: Installing handlers\n"); } termios config; if (!isatty(STDIN_FILENO) || tcgetattr(STDIN_FILENO, &originalConfig) || tcgetattr(STDIN_FILENO, &config)) { throw std::runtime_error("Fail: Setting up input stream\n"); } config.c_lflag &= ~(ICANON | ECHO); config.c_cc[VMIN] = 1; /* Blocking input */ config.c_cc[VTIME] = 0; tcsetattr(STDIN_FILENO, TCSANOW, &config); } ~Game() { tcsetattr(STDIN_FILENO, TCSAFLUSH, &originalConfig); } void run() { std::cout << clearScreen << hideCursor; bool dirty = true; while (!gameOver) { draw(dirty); dirty = input(); dirty = logic() || dirty; } std::cout << showCursor; } }; int main() { Game game; game.run(); }