C input de teclado sem bloqueio

Eu estou tentando escrever um programa em C (no Linux) que faz um loop até que o usuário pressione uma tecla, mas não deve exigir um pressionamento de tecla para continuar cada loop.

Existe uma maneira simples de fazer isso? Eu acho que poderia fazê-lo com select() mas isso parece muito trabalho.

Como alternativa, existe uma maneira de capturar uma tecla ctrlc para fazer a limpeza antes que o programa seja fechado, em vez do io não-bloqueante?

   

    Como já foi dito, você pode usar sigaction para interceptar ctrl-c, ou select para interceptar qualquer input padrão.

    Note, no entanto, que com o último método você também precisa definir o TTY de modo que ele esteja em modo de caractere de cada vez e não de linha de cada vez. O último é o padrão – se você digitar uma linha de texto, ele não será enviado para o stdin do programa em execução até que você pressione enter.

    Você precisaria usar a function tcsetattr() para desativar o modo ICANON e provavelmente também desabilitar o ECHO. Da memory, você também tem que colocar o terminal de volta no modo ICANON quando o programa sair!

    Apenas para completar, aqui está um código que acabei de desmontar (nb: sem verificação de erros!) Que configura um Unix TTY e emula as funções do DOS kbhit() e getch() :

     #include  #include  #include  #include  #include  struct termios orig_termios; void reset_terminal_mode() { tcsetattr(0, TCSANOW, &orig_termios); } void set_conio_terminal_mode() { struct termios new_termios; /* take two copies - one for now, one for later */ tcgetattr(0, &orig_termios); memcpy(&new_termios, &orig_termios, sizeof(new_termios)); /* register cleanup handler, and set the new terminal mode */ atexit(reset_terminal_mode); cfmakeraw(&new_termios); tcsetattr(0, TCSANOW, &new_termios); } int kbhit() { struct timeval tv = { 0L, 0L }; fd_set fds; FD_ZERO(&fds); FD_SET(0, &fds); return select(1, &fds, NULL, NULL, &tv); } int getch() { int r; unsigned char c; if ((r = read(0, &c, sizeof(c))) < 0) { return r; } else { return c; } } int main(int argc, char *argv[]) { set_conio_terminal_mode(); while (!kbhit()) { /* do some work */ } (void)getch(); /* consume the character */ } 

    select() é um pouco baixo demais para conveniência. Eu sugiro que você use a biblioteca ncurses para colocar o terminal no modo cbreak e no modo delay, então chame getch() , que retornará ERR se nenhum caractere estiver pronto:

     WINDOW *w = initscr(); cbreak(); nodelay(w, TRUE); 

    Nesse ponto, você pode chamar o getch sem bloquear.

    Nos sistemas UNIX, você pode usar a chamada sigaction para registrar um manipulador de sinal para o sinal SIGINT , que representa a seqüência de teclas Control + C. O manipulador de sinal pode definir um sinalizador que será verificado no loop, fazendo com que ele seja quebrado apropriadamente.

    Você provavelmente quer kbhit();

     //Example will loop until a key is pressed #include  #include  using namespace std; int main() { while(1) { if(kbhit()) { break; } } } 

    isso pode não funcionar em todos os ambientes. Uma maneira portátil seria criar um segmento de monitoramento e definir algum sinalizador em getch();

    Outra maneira de obter input de teclado sem bloqueio é abrir o arquivo do dispositivo e lê-lo!

    Você precisa conhecer o arquivo do dispositivo que você está procurando, um dos / dev / input / event *. Você pode executar cat / proc / bus / input / devices para encontrar o dispositivo desejado.

    Este código funciona para mim (executado como administrador).

      #include  #include  #include  #include  #include  #include  int main(int argc, char** argv) { int fd, bytes; struct input_event data; const char *pDevice = "/dev/input/event2"; // Open Keyboard fd = open(pDevice, O_RDONLY | O_NONBLOCK); if(fd == -1) { printf("ERROR Opening %s\n", pDevice); return -1; } while(1) { // Read Keyboard Data bytes = read(fd, &data, sizeof(data)); if(bytes > 0) { printf("Keypress value=%x, type=%x, code=%x\n", data.value, data.type, data.code); } else { // Nothing read sleep(1); } } return 0; } 

    A biblioteca de curses pode ser usada para esse propósito. Naturalmente, os manipuladores select() e signal podem ser usados ​​também até certo ponto.

    Se você está feliz apenas pegando Control-C, é um negócio feito. Se você realmente deseja E / S sem bloqueio, mas não deseja a biblioteca de curses, outra alternativa é mover o bloqueio, o estoque e o barril para a biblioteca sfio da AT & T. É uma boa biblioteca modelada no C stdio mas mais flexível, thread-safe e tem melhor desempenho. (sfio significa I / O seguro e rápido.)

    Não existe uma maneira portátil de fazer isso, mas select () pode ser um bom caminho. Veja http://c-faq.com/osdep/readavail.html para mais soluções possíveis.

    Você pode fazer isso usando selecionar como segue:

      int nfds = 0; fd_set readfds; FD_ZERO(&readfds); FD_SET(0, &readfds); /* set the stdin in the set of file descriptors to be selected */ while(1) { /* Do what you want */ int count = select(nfds, &readfds, NULL, NULL, NULL); if (count > 0) { if (FD_ISSET(0, &readfds)) { /* If a character was pressed then we get it and exit */ getchar(); break; } } } 

    Não muito trabalho: D

    Aqui está uma function para fazer isso por você. Você precisa de termios.h que vem com sistemas POSIX.

     #include  void stdin_set(int cmd) { struct termios t; tcgetattr(1,&t); switch (cmd) { case 1: t.c_lflag &= ~ICANON; break; default: t.c_lflag |= ICANON; break; } tcsetattr(1,0,&t); } 

    Quebrando isto abaixo: tcgetattr adquire a informação de tcgetattr atual e armazena isto em t . Se cmd for 1, o sinalizador de input local em t será definido como input sem bloqueio. Caso contrário, é redefinido. Então tcsetattr muda a input padrão para t .

    Se você não redefinir a input padrão no final do seu programa, você terá problemas no seu shell.