paulo1205 escreveu:
Thihup escreveu:
+1
Boa Paulo!
Gostei bastante da explicação, foi útil para mim também.
Mas gostaria de pedir uma coisinha: eu poderia utilizar aquele trecho explicando o que a função system faz? Com os devidos créditos, claro.
Pode. Mas, se quiser, há uma outra postagem minha aqui no VoL que é mais específica a respeito de
quais problemas que podem acontecer, sobretudo quando o comando solicitado não pode ser resolvido diretamente pelo shell. Ela é a terceira mensagem no tópico
http://www.vivaolinux.com.br/topico/C-C++/Duvida-com-realloc-em-C .
Eu comecei a escrever um artigo no meu blog sobre o abuso de
system () há quase um ano, mas nunca o concluí, e por isso não o publiquei.
E posso utilizar o "my_cls" também para ajudar os outros?
E dando uma breve procurada na internet, achei esse trecho de código usando a WinAPI que limpa a tela:
( ... )
É... Eu vi também. Fiquei um pouco surpreso com o tamanho da coisa mas, pensando bem, é isso mesmo que tem de ser feito lá no fundo, e a simplicidade de
putp(clear_screen); (Unix com termcap/terminfo),
initscr() (Unix com curses) ou
clrscr() (DOS ou Windows com ConIO) é, na verdade, apenas aparente. Em última instância, alguém tem de ir lá na tela e colocar um espaço em branco em cada posição endereçável, e depois deslocar o cursor para o canto superior esquerdo. A WinAPI só deixa explícito esse custo (que, contudo, ainda é bem menor do que chamar
system("cls") , até porque o comando CLS vai ter de executar esses mesmos passos) -- o que, no fim das contas, é até didático.
Então, a nossa
my_cls () (e as respectivas inclusões de cabeçalhos) vai evoluindo assim.
#ifdef __unix__
# include <unistd.h>
# include <term.h>
#elif defined(_WIN32)
# include <windows.h>
#elif defined(__DOS__) || defined(__MSDOS__)
# include <conio.h>
#endif
void my_cls(void){
#ifdef __unix__
static bool bad_term=false;
if(!bad_term){
// Caso o terminal ainda não tenha sido inicializado, Inicializa-o.
if(!cur_term){
int err_code;
if(setupterm(NULL, STDOUT_FILENO, &err_code)==ERR){
bad_term=true;
goto term_is_bad;
}
}
putp(clear_screen);
return;
}
#elif defined(_WIN32)
static bool bad_console=false;
static HANDLE hConsole=INVALID_HANDLE_VALUE;
if(!bad_console){
if(hConsole==INVALID_HANDLE_VALUE){
hConsole=GetStdHandle(STD_OUTPUT_HANDLE);
if(hConsole==INVALID_HANDLE_VALUE){
bad_console=true;
goto term_is_bad;
}
}
static COORD top_left_coord = { 0, 0 };
CONSOLE_SCREEN_BUFFER_INFO screen_info;
DWORD screen_size, n_chars_affected;
WORD current_output_attributes;
// Pega tamanho atual da tela (executa toda vez, pois pode mudar).
if(!GetConsoleScreenBufferInfo(hConsole, &screen_info)){
bad_console=true;
goto term_is_bad;
}
screen_size=screen_info.dwSize.X*screen_info.dwSize.Y;
current_output_attributes=screen_info.wAttributes;
// Enche a tela de espaços e depois muda os atributos.
if(
!FillConsoleOutputCharacter(
hConsole, static_cast<TCHAR>(' '),
screen_size, top_left_coord, &n_chars_affected
) ||
!FillConsoleOutputAttribute(
hConsole, current_output_attributes,
screen_size, top_left_coord, &n_chars_affected
) ||
!SetConsoleCursorPosition(hConsole, top_left_coord)
){
bad_console=true;
goto term_is_bad;
}
return;
}
#elif defined(__DOS__) || defined(__MSDOS__)
clrscr();
#endif
#if !defined(__DOS__) && !defined(__MSDOS__)
term_is_bad:
/*
Tipo de terminal desconhecido ou com erro. "Limpa" a tela
somente pulando uma quantidade razoável de linhas, quiçá
arrastando o texto anterior para fora.
*/
for(int i=0; i<100; i++)
putchar('\n');
#endif
}
Sobre esse código, alguns comentários:
* Eu mudei os nomes de variáveis para coisas que me parecem mais descritivas do papel de cada uma. Também deixei de lado aquela notação que usa prefixos indicativos do tipo, pois ela é redundante com uma boa escolha de nomes.
* Eu suprimi a dupla chamada a
GetConsoleScreenBufferInfo (), pegando a informação necessária de atributo no mesmo momento em que pego a informação de tamanho para calcular a quantidade de caracteres a substituir.
* Eu suspeito que o cálculo desses caracteres esteja superestimando a quantidade de caracteres a alterar. Os consoles do Windows frequentemente são configurados de modo a ter um buffer que é maior do que a área exibida na tela, permitindo que se faça a rolagem para baixo ou para cima, o que às vezes é útil para recuperar informação que já deixou a tela. Do jeito como está o programa, parece-me que ele apaga todo o buffer, e não apenas a parte visível na janela, o que talvez não seja desejável. Possivelmente seria mais interessante preservar parte do conteúdo histórico, calculando
top_left_coord em função dos campos
srWindow e/ou
dwMaximumWindowSize de
screen_info . Mas eu não tenho como testar essa hipótese.
* Não testei nada do código para Windows pois tenho nenhuma máquina com Windows com compilador instalado.
* A coisa começou a ficar maior.(e mais feia). O melhor dos mundos seria se todos os sistemas tivessem a mesma interface de programação. Existe, por exemplo, uma implementação nativa de Curses para Windows, chamda
pdcurses . Se ela fosse instalada, também no Windows se poderia usar a solução com
setupterm () e
putp ().
(e a termios.h também). Estranho o.0