Aplicació de consola per capturar events del teclat amb C++ (non-blocking)

De Wikijoan
Dreceres ràpides: navegació, cerca

Contingut

Introducció

Capturar events del teclat a la consola no és trivial, a part de què és dependent del sistema operatiu. Amb SDL ja sé fer aplicacions que capturen els events del teclat, però no és la consola.

Una solució a baix nivell és la que treballa amb el mòdul uinput (com a joy2key_joanillo). He trobat un parell de solucions. La primera és amb ncurses, però no és ben bé la solució que persegueixo doncs utiltiza getch(), que espera que el tecla pitgi la tecla. La segona solució sí que és vàlida.

Aquest codi és per utilitzar-lo en el simulador de la rockola: tinc un servidor mopidy-mpd amb el client mpc, que sap fer playback de les cançons que estan a la llista. D'altra banda, vull simular apretant la tecla 'c' que fico una moneda, i a conseqüència d'això s'afegeixen dues cançons a la llista de reproducció.

Solució ncurses (no és non-blocking)

press_key.cpp:

// http://www.creditcrunch.co.uk/topic/10995-c-detect-updownleftright-keys-for-console-application-linux/
/* example.cpp */
// $ g++ press_key.cpp -I /usr/include -lncurses -o press_key

#include <unistd.h>
#include <stdlib.h>
#include <ncurses.h>

#define LOCAL_ESCAPE_KEY 27

int main(void)
{

int key;

initscr();
crmode();
keypad(stdscr, TRUE);
noecho();
clear();
mvprintw(5,5, "Keypad demonstration. Press 'q' to quit");
move(7,5);
refresh();
key = getch();

while(key != ERR && key != 'q') {
        move(7,5);
        clrtoeol();

        if ((key >= 'A' && key <= 'Z')||(key >= 'a' && key <= 'z')){
        printw("Key was %c", (char)key);
        }
        else {
        switch(key){
        case LOCAL_ESCAPE_KEY: printw("%s", "Escape key"); break;
        case KEY_END: printw("%s", "END key"); break;
        case KEY_RIGHT: printw("%s", "RIGHT key"); break;
        case KEY_LEFT: printw("%s", "LEFT key"); break;
        case KEY_UP: printw("%s", "UP key");break;
        case KEY_DOWN: printw("%s", "DOWN key"); break;
        default: printw("Unmatched - %d", key); break;
        }/* switch */
        }/*else*/
        
        refresh();
        key = getch();
        }/* while */
        endwin();
exit(EXIT_SUCCESS);
};
$ g++ press_key.cpp -I /usr/include -lncurses -o press_key
$ ./press_key

Solució non-blocking

press_key_v2.cpp:

//non-blocking code (no utiltiza getch())
//http://www.cplusplus.com/forum/general/5304/
// $ g++ press_key_v2.cpp -o press_key

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>

int main()
{
    struct termios oldSettings, newSettings;

    tcgetattr( fileno( stdin ), &oldSettings );
    newSettings = oldSettings;
    newSettings.c_lflag &= (~ICANON & ~ECHO);
    tcsetattr( fileno( stdin ), TCSANOW, &newSettings );

        while ( 1 )
        {
                fd_set set;
                struct timeval tv;

                tv.tv_sec = 10;
                tv.tv_usec = 0;

                FD_ZERO( &set );
                FD_SET( fileno( stdin ), &set );

                int res = select( fileno( stdin )+1, &set, NULL, NULL, &tv );

                if( res > 0 )
                {
            char c;
                        printf( "Input available\n" );
            read( fileno( stdin ), &c, 1 );
                }
                else if( res < 0 )
                {
                        perror( "select error" );
            break;
                }
                else
                {
                        printf( "Select timeout\n" );
                }
        }

    tcsetattr( fileno( stdin ), TCSANOW, &oldSettings );
        return 0;
}
$ g++ press_key_v3.cpp -o press_key_v3
$ ./press_key_v3 
Input available
Input available
...
Eines de l'usuari
Espais de noms
Variants
Accions
Navegació
IES Jaume Balmes
Màquines recreatives
CNC
Informàtica musical
joanillo.org Planet
Eines