Pidiendo una tecla

Leer una sola tecla de la consola puede parecer un hecho trivial. En DOS, en Windows, se usa simplemente la función getch, o getche (funciones del viejo "conio.h"). Sin embargo en Linux (en Unix en general) resulta no serlo. El driver de la terminal acepta la entrada de a una línea entera por vez, y si ejecutamos read(0, buf, 1), ese 1 no hace que read termine al pulsarse una tecla. Esto viene de la época en que las computadoras se usaban desde terminales unidas a la computadora por una conexión no muy rápida, entonces era un desperdicio enviar cada tecla pulsada.

Para lograr que la terminal envíe una tecla a la vez hay que cofigurar la terminal adecuadamente, esto se logra con funciones de termios(2). En concreto: es necesario desactivar el flag ICANON. Información sobre este tipo de flags puede encontrarse en la manpage de stty.

Ejemplo:

#include <stdio.h>
#include <termios.h>
#include <unistd.h>

int main(int argc, char **argv)
{
        struct termios t;
        int c;

	/* Obtenemos la configuración actual de la terminal */
        tcgetattr(0, &t);

	/* Le apagamos el bit "ICANON" */
        t.c_lflag &= ~ICANON;

	/* Establecemos la nueva configuración de la terminal */
        tcsetattr(0, TCSANOW, &t);

        printf("Una tecla..: ");
        fflush(stdout);
        c = getchar();
        printf("\n%d\n", c);
        
        /* ¡Ojo! ¡Si no dejamos todo como estaba le dejamos
         * al usuario la terminal rota!
         */
        t.c_lflag |= ICANON;
        tcsetattr(0, TCSANOW, &t);

        return 0;
}

Un detalle ya que estamos. Note que después del printf llamo a fflush. Esto es porque cuando la salida está conectada a una terminal (y no a un pipe) muchas funciones sacan la salida de a líneas. Entonces necesitamos fflush para que esto no sea así.


También puede ser necesario evitar que Linux muestre la tecla pulsada automáticamente. Esto de mostrar la tecla pulsada se llama eco. Para desconectar el eco en la terminal se procede de manera similar a lo ya dicho. La diferencia es sólo que el flag a apagar no es ICANON si no ECHO. Así:

...
        tcgetattr(0, &t);
        t.c_lflag &= ~ECHO;
        tcsetattr(0, TCSANOW, &t);
...

Más información

Si se quieren hacer cosas más complicadas es conveniente ver qué bibliotecas ya existen. Por ejemplo, para hacer interfaces de linea de comando conviene investigar la biblioteca readline. Esta biblioteca soporta historial de comandos, edición, completar la línea inteligentemente cuando se pulse tab, etc. Readline viene con todas las distribuciones.

Si lo que se quiere es hacer una aplicación completamente de consola, a pantalla completa, entonces lo que se necesita es curses. Este paquete viene con todas las distribuciones, ver las páginas de manual (man ncurses). Empecé una introducción a cómo pueden hacerse aplicaciones de consola. Para los que vienen del mundo Windows/DOS: Curses sería un reemplazo completo de todas las funciones que se encuentran en conio.h, pero para Unix.


Volver al índice.


Por Nicolás Lichtmaier. Cualquier comentario o pedido de mayor claridad o extensión será bien recibido.