Keypressed

/*
 * keypressed - Perform some calculation until the user presses some key.
 *
 * By default, standard input in C++ (and most terminals) is line-buffered:
 * the terminal waits until you press Enter before sending the input to your
 * program.
 *
 * Checking for a key press is not part of standard C++; it requires
 * platform-specific APIs. On Windows, you can use _kbhit() from <conio.h>.
 * The Microsoft-specific function name kbhit() is a deprecated alias for
 * _kbhit(). The name is deprecated because it doesn't follow the Standard
 * C rules for implementation-specific names. Therefore, Microsoft recommends
 * you use _kbhit() instead.
 * On Linux, you need termios to switch the terminal to raw mode (disable
 * canonical input buffering) and check for a key press. Note that anything
 * send to stdout while in raw mode is buffered and printed after leaving
 * raw mode.
 *
 * If any type of portability is needed, use ncurses. It is a programming
 * library for creating textual user interfaces (TUIs) that work across a
 * wide variety of terminals.
 *
 *  g++ -std=c++17 -Wpedantic -Wall -Wextra keypressed.cpp -o keypressed
*/


#include <iostream>
#include <chrono>
#include <thread>


#if defined(_WIN32) || defined(_WIN64)
// -------------------- WINDOWS VERSION --------------------
#include <conio.h>

char get_char() {
    return _getch();
}

#else
// -------------------- LINUX / POSIX VERSION --------------------
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>

class NonBlockingRawModeScope {
public:
    NonBlockingRawModeScope() {
        tcgetattr(STDIN_FILENO, &orig_termios);
        termios raw = orig_termios;
        raw.c_lflag &= ~(ICANON | ECHO);   // disable canonical mode and echo
        tcsetattr(STDIN_FILENO, TCSANOW, &raw);

        // Make stdin non-blocking
        int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
        fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
    }

    ~NonBlockingRawModeScope() {
        tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
    }

private:
    termios orig_termios;
};

int _kbhit() {
    unsigned char ch;
    int nread = read(STDIN_FILENO, &ch, 1);

    if (nread == 1) {
        ungetc(ch, stdin);
        return 1;
    }

    return 0;
}

char get_char() {
    return getchar();
}
#endif


int main() {
    using namespace std::chrono_literals;

    std::cout << "Press any key to stop...\n";

    int counter = 0;
#if !defined(_WIN32) && !defined(_WIN64)
    NonBlockingRawModeScope rawMode;
#endif
    for (;;) {
        if (_kbhit()) {
            char c = get_char();
            std::cout << "\nYou pressed: " << c << "\n";
            break;
        }
        counter++;
        std::cout << "Working... counter = " << counter << "\r" << std::flush;
        std::this_thread::sleep_for(10ms);
    }

    return 0;
}