ayuda con kbhit()

Iniciado por 0xDani, 19 Mayo 2012, 17:08 PM

0 Miembros y 1 Visitante están viendo este tema.

0xDani

Hola bueno veran he estado buscando como emular la funcion kbhit() de conio.h en ubuntu, yhe encontrado referencias a curses y ncurses, ademas en este foro me las han recomendado, pero no encuentro la forma de emular esta funcion, agradeceria mucho ayuda. :huh:

Saludos :D
I keep searching for something that I never seem to find, but maybe I won't, because I left it all behind!

I code for $$$
Hago trabajos en C/C++
Contactar por PM

RyogiShiki

#1
Hola.

Bueno, hacia bastante tiempo no tocaba a ncurses, buscando un poco he encontrado un ejemplo en StackOverflow que emula la función kbhit() y como veo que no conoces aun ncurses, pues paso a explicar el ejemplo:

#include <ncurses.h>
#include <unistd.h>  /* only for sleep() */

int kbhit(void)
{
   int ch = getch();

   if (ch != ERR) {
       ungetch(ch);
       return 1;
   } else {
       return 0;
   }
}

int main(void)
{
   initscr();

   cbreak();
   noecho();
   nodelay(stdscr, TRUE);

   scrollok(stdscr, TRUE);
   while (1) {
       if (kbhit()) {
           printw("Key pressed! It was: %d\n", getch());
           refresh();
       } else {
           printw("No key pressed yet...\n");
           refresh();
           sleep(1);
       }
   }
}


El principio del programa:
#include <ncurses.h>
#include <unistd.h>  /* only for sleep() */

incluimos como no ncurses que es la que nos va a permitir interactuar y generar respuesta a las acciones del teclado. Incluimos también unistd que nos da accceso a la API POSIX del sistema, aunque en este caso solo vamos usar como se ve en el omentario la función sleep()

Bueno, empecemos por la función main antes de ir a la función kbhit:

initscr();
Esta función es la que va a inicializar el modo curses ni no llamamos a esta función no podremos usar el resto de ncurses.

cbreak();
Esta función deshabilita el "line buffering" del terminal, lo que quiere decir que no es necesario hacer retorno de carro o tener un caracter de nueva linea al final, sino que los caracteres que tipeamos en el teclado son enviados directamente.

noecho();
esta función deshabilita el "eco" del terminal, lo que significa que los caracteres que se ingresan no son mostrados, similar a cuando se ingresa una contraseña desde la terminal.

nodelay(stdscr, TRUE);
esta función convierte a la función getch() en una llamada no bloqueante, lo que quiere decir que cuando se usa getch() el programa no se va a parar a esperar a que el usuario ingrese algo, si no se ingresa nada entonces la función devuelve ERR y si se ingresa algo entonces almacena el código de la tecla que fue ingresada. Esta función acepta dos parámetros stdscr y la representación de un booleano (TRUE o FALSE). Verás cuando se trabaja con ncurses se usa el concepto de WINDOWS que no son nada más que espacios independientes en donde se puede disponer de la información que queramos que el usuario conozca, igualmente podemos cerrar eliminar o crear nuevas WINDOW. por defecto ncurses establece que la ventana principal es stdscr. Entonces en definitiva lo que hacemos con la función es decir que queremos que getch no sea bloqueante unicamente en stdscr si tuviéramos otras WINDOWS no serian afectadas por esta función, y por supuesto para deshabilitar con un simple nodelay(stdscr, FALSE).

scrollok(stdscr, TRUE);
VA a evitar que se presente scroll fuera de los limites del tamaño actual de la terminal, osea que verás que el buffer de lo que se muestra en pantalla no se almacena y nunca habrá scroll, como lo explique antes esto se aplica a stdscr y para deshabilitar con scrollok(stdscr, FALSE);

Antes de continuar a revisar el loop revisemos la función kbhit:
int kbhit(void)
{
   int ch = getch();

   if (ch != ERR) {
       ungetch(ch);
       return 1;
   } else {
       return 0;
   }
}

Entonces vemos que es una función que retorna un entero, en este caso 1 o 0 y no recibe ningún parámetro. Estamos almacenando lo que retorna getch() en ch, para el momento en el que llamemos a kbhit() getch no debería ser bloqueante. comprobamos si ch es diferente de ERR, entonces se habrá producido una pulsación, pero cuando llamamos a getch el código de la tecla habrá sido sacada de la cola de entrada, lo cual es un problema, así que para devolverla hacemos un ungetch(ch), así la próxima vez que llamemos a getch() nos devolvera el código de la tecla que estaba en ch. Si recibimos ERR no habrá pulsación alguna y retornamos 0.

Ahora al bucle:
while (1) {
       if (kbhit()) {
           printw("Key pressed! It was: %d\n", getch());
           refresh();
       } else {
           printw("No key pressed yet...\n");
           refresh();
           sleep(1);
       }
   }


Entonces comenzamos un bucle infinito, y probamos el retorno de kbhit, si kbhit retorna 1 entonces habrá una pulsación, imprimimos que se ha presentado una pulsación y obtenemos el código que estará en getch() (gracias a ungetch()), así si presionamos la tecla 'a' por ejemplo tendremos una salida como la siguiente:
CitarKey pressed! It was: 97
donde 97 es el código ASCII de 'a'

Si kbhit devuelve 0 entonces no habrá pulsación y tendremos una salida como la siguiente:
CitarNo key pressed yet...

refresh()
ncurses nos permite reducir la latencia del terminal. Si queremos que los cambios que hemos hecho se muestren por pantalla es neesario usar refresh, si no lo hacemos no se mostrará ningún cambio que se haya producido (en este caso en stdscr).

sleep(1)
finalmente para poder ver que es lo que estamos haciendo, y no tener un loop loco en la pantalla dormimos el programa por 1 segundo, tiempo más que suficiente para ver que es lo que sucede.

Ahora creo que estás en ubuntu, así que lo primero es instalar la librería ncurses para desarrollo y si no estoy mal sería:
Código (bash) [Seleccionar]
sudo apt-get install ncurses-dev

Luego para compilar usamos el siguiente comando:
Código (bash) [Seleccionar]
gcc code.c -o code -lncurses

y para ejecutar y ver el resultado:
Código (bash) [Seleccionar]
./code

Espero haya sido de ayuda.

Saludos


0xDani

Muchas gracias, si que ha sido de mucha ayuda, me has resuelto todas mis dudas de un tiron, y ademas me has explicado el code paso a paso, que mas que dar las gracias y quitarse el sombrero;-)

I keep searching for something that I never seem to find, but maybe I won't, because I left it all behind!

I code for $$$
Hago trabajos en C/C++
Contactar por PM

RyogiShiki

Bien, me alegro que haya sido de ayuda, cualquier duda no dudes en preguntar que para eso esta la comunidad.

Saludos