A termios.h é uma biblioteca especificada pelo padrão POSIX que permite que nós alteremos as opções do terminal em que o programa está sendo executado. O exemplo mais típico é quando vamos fazer o login em um console virtual e quando digitamos a senha não aparece nada; isso pode ser feito tirando a “opção” ECHO do terminal que estamos usando.
Exemplos
Detectando eventos no terminal
Digamos que um software esteja sendo desenvolvido (para o terminal) e que em um dado momento, o usuário deve escolher uma opção, alguma coisa parecida com isto:
Houve um erro na hora de executar asdf:
[R]epetir [C]ontinuar [A]bortar
Usando getchar ou scanf ou qualquer outra função da biblioteca stdio.h, a leitura será feita linha por linha. Por isso, mesmo que nós queiramos apenas um caractere, o usuário terá que apertar esse caractere e depois Enter. Eu queria uma forma de fazer isso em que o usuário simplesmente digitasse ‘r’ ou ‘c’, enfim, apenas uma tecla. Pensei em usar ncurses até, mas achei essa solução exagerada, e além disso, como o ncurses faz isso?
A solução apareceu com o uso de termios.h e de uma função de leitura também especificada pelos padrões POSIX, mas na biblioteca unistd.h, a função read().
Código
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <termios.h> void silent(struct termios *init_setting, struct termios *new_setting); void verbose(struct termios *init_setting); unsigned char get_char(void); int main(int argc, char *argv[]) { struct termios init_setting, new_setting; unsigned char ch = 0; printf("Houve um erro na execução de foo.\n[C]ontinuar\t[R]epetir\t[A]bortar\n"); silent(&init_setting, &new_setting); while(ch != 'a' && ch != 'A') { ch = get_char(); switch(ch) { case 'r': case 'R': verbose(&init_setting); printf("Repetindo...\n"); printf("Houve um erro na execução de foo.\n[C]ontinuar\t[R]epetir\t[A]bortar\n"); silent(&init_setting, &new_setting); break; case 'c': case 'C': verbose(&init_setting); printf("Continuando...\n"); sleep(2); printf("Houve um erro na execução de foo.\n[C]ontinuar\t[R]epetir\t[A]bortar\n"); silent(&init_setting, &new_setting); break; } } verbose(&init_setting); return(0); } void silent(struct termios *init_setting, struct termios *new_setting) { tcgetattr(fileno(stdin), init_setting); new_setting = init_setting; new_setting->c_lflag &= ~(ICANON | ECHO | ISIG); new_setting->c_cc[VMIN] = 1; new_setting->c_cc[VTIME] = 0; tcsetattr(fileno(stdin), TCSADRAIN, new_setting); } void verbose(struct termios *init_setting) { tcsetattr(fileno(stdin), TCSADRAIN, init_setting); } unsigned char get_char() { unsigned char ch; read(fileno(stdin), &ch, sizeof(unsigned char)); return ch; }
Explicações
Para que a idéia funcione, precisamos alterar algumas opções do terminal, e é aí que entra a biblioteca termios.h. Vamos definir duas funções, verbose(), que volta as configurações ao normal, e silent(), que tira o echo do terminal. O comportamento desejado é que quando o usuário aperte uma tecla, ela não apareça na tela, mas isso traz uma consequência: se você tentar usar o printf() ou qualquer outra função do tipo, ela também não aparecerá. Por isso, temos que ficar alternando entre os modos.
O echo é desativado na linha “new_setting->c_lflag &= ~(ICANON | ECHO);“, onde nós também desligamos o modo canônico, o que nos permite fazer a leitura caractere por caractere, ao invés de linha por linha, como é o padrão.
Outro detalhe importante são as linhas new_setting->c_cc[]. A função read() é uma função que bloqueia a execução do programa até que certa condição seja encontrada. No caso, definimos que ela só pode retornar após ler 1 caractere (VMIN), não importa quanto tempo (em décimos de segundo) isso demore (VTIME). Neste exemplo, poderíamos também usar c_cc[VMIN] = 0 e c_cc[VTIME] = 100, que faz com que a função retorne assim que receber o primeiro caractere ou depois de 10 segundos, permitindo que o programa continue com uma opção padrão.
Um último detalhe é o tipo de loop que eu usei. De maneira geral, um loop do tipo while(1) pode maximizar o uso de uma das CPUs, mas no caso não temos esse problema, já que read() é uma função que bloqueia a execução do programa até que ela retorne.
Calculadora RPN
Uma calculadora que usa notação polonesa reversa para o terminal que eu escrevi usando a biblioteca usando termios.h





