C entrada de teclado sin locking

Intento escribir un progtwig en C (en Linux) que se repite hasta que el usuario presiona una tecla, pero no debe requerir una pulsación de tecla para continuar cada ciclo.

¿Hay una manera simple de hacer esto? Me imagino que podría hacerlo con select() pero parece mucho trabajo.

Alternativamente, ¿hay alguna manera de capturar una tecla Ctrlc para hacer la limpieza antes de que el progtwig se cierre en lugar de no bloquearlo?

Como ya se dijo, puede usar sigaction para sigaction ctrl-c, o select atrapar cualquier entrada estándar.

Sin embargo, tenga en cuenta que con este último método también debe configurar el TTY para que esté en modo de carácter a la vez en lugar de línea a la vez. El último es el predeterminado: si escribe una línea de texto, no se envía al stdin del progtwig en ejecución hasta que presione enter.

tcsetattr() que usar la función tcsetattr() para desactivar el modo ICANON, y probablemente también deshabilite ECHO. ¡De la memoria, también tiene que volver a poner el terminal en el modo ICANON cuando el progtwig sale!

Solo para completar, aquí hay un código que acabo de quitar (nb: ¡sin comprobación de errores!) Que configura un TTY de Unix y emula las funciones de DOS kbhit() y 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() es un nivel demasiado bajo para su conveniencia. Le sugiero que use la biblioteca ncurses para poner el terminal en el modo cbreak y el modo de demora, luego llame a getch() , que devolverá ERR si no hay un carácter listo:

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

En ese momento puedes llamar a getch sin bloquear.

En los sistemas UNIX, puede usar la llamada sigaction para registrar un manejador de señal para la señal SIGINT que representa la secuencia de teclas Control + C. El manejador de señal puede establecer un indicador que se comprobará en el bucle, lo que hará que se rompa adecuadamente.

Probablemente quieras kbhit();

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

esto puede no funcionar en todos los entornos. Una forma portátil sería crear un hilo de supervisión y establecer un indicador en getch();

Otra forma de obtener una entrada de teclado sin locking es abrir el archivo del dispositivo y leerlo.

Debe conocer el archivo del dispositivo que está buscando, uno de / dev / input / event *. Puede ejecutar cat / proc / bus / input / devices para encontrar el dispositivo que desea.

Este código funciona para mí (se ejecuta 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; } 

La biblioteca de curses se puede usar para este propósito. Por supuesto, los manejadores select() y de señal también se pueden usar hasta cierto punto.

Si estás contento de haber alcanzado Control-C, ya está hecho. Si realmente quiere E / S sin locking pero no quiere la biblioteca de curses, otra alternativa es mover el locking, el stock y el barril a la biblioteca sfio AT & T. Es una bonita biblioteca con patrón de C stdio pero más flexible, segura para subprocesos y tiene un mejor rendimiento. (sfio significa E / S segura y rápida).

No hay una forma portátil de hacerlo, pero select () podría ser una buena manera. Consulte http://c-faq.com/osdep/readavail.html para obtener más soluciones posibles.

Puedes hacer eso usando seleccionar de la siguiente manera:

  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; } } } 

No mucho trabajo: D

Aquí hay una función para hacer esto por ti. Necesitas termios.h que viene con los 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); } 

Rompiendo esto: tcgetattr obtiene la información del terminal actual y la almacena en t . Si cmd es 1, el indicador de entrada local en t se configura en entrada no bloqueante. De lo contrario, se restablece. Entonces tcsetattr cambia la entrada estándar a t .

Si no restablece la entrada estándar al final de su progtwig, tendrá problemas en su caparazón.

    Intereting Posts