Funciones varias de conio.h funcionando en Windows y Linux

Conio.h es una librería que venía con los compiladores Turbo C++ de Borland. En ella, podíamos encontrar funciones extremadamente útiles y bastante cómodas para el manejo de entrada y salida por consola (console input output).

Para desarrollar software de consola con las posibilidades que ofrecían, tenemos a nuestra disposición para Linux nCurses. He trabajado con ellas, y las recomiendo muchísimo. Creo haber leído por ahí que había una versión para Windows.

Ahora bien: se puede dar el caso de que no quieras recurrir a estas librerías por varias razones:

  • No querer utilizar librerías externas
  • No depender del código de otra persona

Y un largo etcétera. En mi caso, tengo varios pequeños motivos. Quiero publicar un software multiplataforma en C++ y sólo necesitaba en su momento emular unas pocas funciones concretamente.

Picoteando aquí y allá, toqueteando y juntando las distintas cosas que encontré, me hice un par de métodos muy majos para la API de mi proyecto.

La primera de todas es una función de tipo sleep(). Es completamente ANSI C, por lo que debería funcionaros en cualquier plataforma (siento que wordpress se coma las tabulaciones, veré de solucionarlo):

void sleep(unsigned double seconds)
{
    long ticks;
    ticks = clock() seconds*CLOCKS_PER_SEC;
    while (clock()<ticks);
}

Se le pasa como parámetro el tiempo en segundos que se desea para hacer la pausa, pudiendo añadir decimales . Si se pulsan caracteres durante la espera, se recogerán nada mas acabar esta.  Para solucionarlo, se puede añadir la siguiente línea de código después de la función:
sleep(5.0);
fflush(stdin);

Esto limpiará el buffer de teclado y evitará esas molestas teclas fantasma.

Otra de estas que me preparé fue clrscr(). No utiliza ANSI C, pero la versión de Linux va con secuencias de escape, así que debería funcionar en cualquier distro. Admito que esta en concreto no es más que una burda copia y pega retocada y lo siento, pero aún no me he metido con la API de Windows, y me queda por estudiar este pedacito de Linux…

void clrscr()
{
#ifdef WIN32
    DWORD n;                         /* Number of characters written */
    DWORD size;                      /* number of visible characters */
    COORD coord = {0};               /* Top left screen position */
    CONSOLE_SCREEN_BUFFER_INFO csbi;

    /* Get a handle to the console */
    HANDLE h = GetStdHandle ( STD_OUTPUT_HANDLE );

    GetConsoleScreenBufferInfo ( h, &csbi );

    /* Find the number of characters to overwrite */
    size = csbi.dwSize.X * csbi.dwSize.Y;

    /* Overwrite the screen buffer with whitespace */
    FillConsoleOutputCharacter ( h, TEXT ( ' ' ), size, coord, &n );
    GetConsoleScreenBufferInfo ( h, &csbi );
    FillConsoleOutputAttribute ( h, csbi.wAttributes, size, coord, &n );

    /* Reset the cursor to the top left position */
    SetConsoleCursorPosition ( h, coord );
#else
    char str[] = " [H [2J";
    str[3] = str[0] = 27;
    write(1, str, 7);
#endif

Testeado en Windows XP y Ubuntu 9.10. Ni un solo problema de compilación ni ejecución [¡Y yo solito hice esa mierda de precompilado (que a saber como se comportará en una distro que no sea ni Windows ni GNU/Linux xD)!].

Y ahora, la más cabrona de todas, getch():

#ifdef WIN32
    int car;
    DWORD leidos, modo;


    GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &modo);
    SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), modo & !ENABLE_ECHO_INPUT & !ENABLE_PROCESSED_INPUT);
    ReadConsole(GetStdHandle(STD_INPUT_HANDLE), &car, 1, &leidos, NULL);
    SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), modo);
    return car;
#else
    struct termios oldt, newt;
    int ch;
    tcgetattr( STDIN_FILENO, &oldt );
    newt = oldt;
    newt.c_lflag &= ~( ICANON | ECHO );
    tcsetattr( STDIN_FILENO, TCSANOW, &newt );
    ch = getchar();
    tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
    return ch;
#endif

Aquí la API de Windows la pude entender un poquito más. La de Linux, sin embargo, es más interesantilla. Toquetearé ese código cuando haya hecho mi “tarea” :3

Esta función también la he probado a compilar en ambos sistemas operativos, y da los resultados deseados. Eso sí, se comporta como el getch() de borland, y no como el de nCurses. Es decir, sin eco.

Si queréis eco, no tenéis más que recoger el caracter devuelto por la función e imprimirlo por pantalla. Ahora, que podemos derrochar memoria, ya no estamos en los noventa…

#ifdef WIN32
    char car;
    DWORD leidos, modo;

    GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &modo);
    SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), modo & !ENABLE_ECHO_INPUT & !ENABLE_PROCESSED_INPUT);
    ReadConsole(GetStdHandle(STD_INPUT_HANDLE), &car, 1, &leidos, NULL);
    SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), modo);

    std::cout << car;
    return car;
#else
    struct termios oldt, newt;
    char ch;

    tcgetattr( STDIN_FILENO, &oldt );
    newt = oldt;
    newt.c_lflag &= ~( ICANON | ECHO );
    tcsetattr( STDIN_FILENO, TCSANOW, &newt );
    ch = getchar();
    tcsetattr( STDIN_FILENO, TCSANOW, &oldt );

    std::cout << ch;
    return ch;
#endif

Otra clásica función de Borland. Hace lo mismo que getch(), pero muestra la tecla pulsada. Si es que lo daban todo mascaico…

Bueno. Algunos visitantes habrán encontrado este post realmente útil, y a otros les habrá parecido un auténtico pedo. Lo publico simplemente porque me tuve que hacer esta recopilación y no encontré por ningún lado nada parecido. Espero que haya servido de algo para alguien…

Un saludo a todos, y espero escribir aquí con más frecuencia ;)

Advertisement
    • ElFaloEdmund
    • 16/04/10

    void sleep(double seconds)
    {
    long ticks; [...]

    Que tal long unsigned? O esperas hacer esperas con duración negativa? xD

  1. Aún no hay trackbacks

Deja un comentario

Fill in your details below or click an icon to log in:

Logo de WordPress.com

You are commenting using your WordPress.com account. Log Out / Cambiar )

Twitter picture

You are commenting using your Twitter account. Log Out / Cambiar )

Facebook photo

You are commenting using your Facebook account. Log Out / Cambiar )

Connecting to %s

Seguir

Get every new post delivered to your Inbox.