web-dev-qa-db-fra.com

Utilisation de kbhit () et getch () sous Linux

Sous Windows, j'ai le code suivant pour rechercher une entrée sans interrompre la boucle:

#include <conio.h>
#include <Windows.h>
#include <iostream>

int main()
{
    while (true)
    {
        if (_kbhit())
        {
            if (_getch() == 'g')
            {
                std::cout << "You pressed G" << std::endl;
            }
        }
        Sleep(500);
        std::cout << "Running" << std::endl;
    }
}

Cependant, vu qu'il n'y a pas de conio.h, quel est le moyen le plus simple de réaliser cette même chose sous Linux?

9
Boxiom

Le ncurses howto cité ci-dessus peut être utile. Voici un exemple illustrant comment ncurses pourrait être utilisé comme l'exemple conio:

#include <ncurses.h>

int
main()
{
    initscr();
    cbreak();
    noecho();
    scrollok(stdscr, TRUE);
    nodelay(stdscr, TRUE);
    while (true) {
        if (getch() == 'g') {
            printw("You pressed G\n");
        }
        napms(500);
        printw("Running\n");
    }
}

Notez qu'avec ncurses, l'en-tête iostream n'est pas utilisé. En effet, le mélange de stdio avec ncurses peut avoir des résultats inattendus.

ncurses, soit dit en passant, définit TRUE et FALSE. Un ncurses correctement configuré utilisera le même type de données pour bool de ncurses que le compilateur C++ utilisé pour configurer ncurses.

7
Thomas Dickey

Si votre Linux n'a pas de conio.h Qui supporte kbhit() vous pouvez regarder ici pour le code de Morgan Mattews pour fournir la fonctionnalité kbhit() d'une manière compatible avec tout système compatible POSIX.

Comme l'astuce désactive la mise en mémoire tampon au niveau des termios, elle devrait également résoudre le problème de la getchar() comme démontré ici .

12
Christophe

Une solution compacte basée sur la réponse de Christophe est

#include <sys/ioctl.h>
#include <termios.h>

bool kbhit()
{
    termios term;
    tcgetattr(0, &term);

    termios term2 = term;
    term2.c_lflag &= ~ICANON;
    tcsetattr(0, TCSANOW, &term2);

    int byteswaiting;
    ioctl(0, FIONREAD, &byteswaiting);

    tcsetattr(0, TCSANOW, &term);

    return byteswaiting > 0;
}

Contrairement à cette réponse, cela ne laissera pas le terminal dans un état étrange après la fin du programme. Cependant, il laisse toujours les caractères assis dans le tampon d'entrée, de sorte que la touche qui a été enfoncée apparaîtra de manière indésirable sur la ligne d'invite suivante.

Une solution différente qui résout ce problème est

void enable_raw_mode()
{
    termios term;
    tcgetattr(0, &term);
    term.c_lflag &= ~(ICANON | ECHO); // Disable echo as well
    tcsetattr(0, TCSANOW, &term);
}

void disable_raw_mode()
{
    termios term;
    tcgetattr(0, &term);
    term.c_lflag |= ICANON | ECHO;
    tcsetattr(0, TCSANOW, &term);
}

bool kbhit()
{
    int byteswaiting;
    ioctl(0, FIONREAD, &byteswaiting);
    return byteswaiting > 0;
}

L'utilisation est la suivante

enable_raw_mode();
// ...
if (kbhit()) ...
// ...
disable_raw_mode();
tcflush(0, TCIFLUSH); // Clear stdin to prevent characters appearing on Prompt

Désormais, aucun caractère saisi entre l'exécution de la première et de la dernière ligne n'apparaîtra dans le terminal. Cependant, si vous quittez avec Ctrl + C, le terminal est laissé dans un état étrange. (Soupir)

6
PBS

Alors que l'utilisation de ncurses est fonctionnellement équivalente à l'API Turbo C "conio.h", une solution plus complète consiste à utiliser une implémentation conio, comme cela peut être trouvé ici .

Vous le téléchargez et l'utilisez dans votre programme pour une implémentation très complète de l'interface conio, sous Linux. (Ou OSX.) Écrit par Ron Burkey.

3
Prof. Falken