#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <termios.h>
#include <unistd.h>
char kbPeek(int msTimeOut)
{
/* Antiguo y nuevo estado de la terminal con y sin modo ICANON */
struct termios prevTIOS;
struct termios newTIOS;
/* Agregamos el timeout a una estructura timeval */
struct timeval tvTimeout;
tvTimeout.tv_usec = msTimeOut * 1000; /* mili -> micro */
/* Armamos un fd_set con STDIN para pasarlo a select() */
fd_set readSet;
FD_ZERO(&readSet);
FD_SET(STDIN_FILENO,&readSet);
/* Guardamos el termios original con el modo canonico levantado
* y lo desactivamos, pasando a modo RAW */
tcgetattr(0,&prevTIOS);
memcpy(&newTIOS
,&prevTIOS
,sizeof(struct termios
));
cfmakeraw(&newTIOS);
tcsetattr(0,TCSANOW,&newTIOS);
/* Pedimos a select que revise el descriptor de fichero STDIN
esperando hasta determinado tiempo puesto en tvTimeOUT */
int retval = select(1,&readSet,0,0,&tvTimeout);
if ( retval == -1)
perror("select()"); /* Algo salio mal */
else if (retval == 0)
return 0; /* No paso nada */
/* Si paso algo ... usemos read() para pedir un caracter */
char peekedChar;
read(STDIN_FILENO,&peekedChar,1);
/* Volvemos al modo canonico y retornamos valor */
tcsetattr(0,TCSANOW,&prevTIOS);
return peekedChar;
}
int main(int argc, char *argv[])
{
/* Aca guardamos cuanto queremos esperar y el caracter en cuestion */
int msTimeout = 900;
char readValue;
/* Usamos \r\n por que solo dentro de kbPeek() estamos en modo RAW */
printf("Esperando %dms una tecla ...\r\n",msTimeout
);
/* Largamos kbPeek*/
readValue = kbPeek(msTimeout);
/* Mostramos */
printf("Leido : %c \r\n", readValue
);
return EXIT_SUCCESS;
}
No recuerdo cuan portable es termios (dentro de *nix, en teoría es algo *portable*), though ... de todas maneras, si tenés un código Windows, vas a tener que usar preprocesador te guste o no, así que la portabilidad es lo de menos si podés agregar diferentes implementaciones vía preprocessor.
Saludos.