c - guia - qgis manual
C entrada de teclado sin bloqueo (10)
Aquí hay una función para hacer esto por ti. Necesitas termios.h
que viene con los sistemas POSIX.
#include <termios.h>
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 programa, tendrá problemas en su caparazón.
Intento escribir un programa 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 Ctrl - c para hacer la limpieza antes de que el programa 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 programa 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 programa 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 <conio.h>
kbhit()
y getch()
:
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <termios.h>
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 */
}
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.
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.
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.
Otra forma de obtener una entrada de teclado sin bloqueo 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 <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/input.h>
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;
}
Probablemente quieras kbhit();
//Example will loop until a key is pressed
#include <conio.h>
#include <iostream>
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();
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
Si estás contento de haber alcanzado Control-C, ya está hecho. Si realmente quiere E / S sin bloqueo pero no quiere la biblioteca de curses, otra alternativa es mover el bloqueo, 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).
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.