Como ler dados do teclado sem interromper execução do programa ?

1. Como ler dados do teclado sem interromper execução do programa ?

Lucas Rodrigo Bento Souza
luccasrodge

(usa Ubuntu)

Enviado em 06/12/2014 - 20:55h

Estou querendo ler as setas do teclado (cima,baixo,esquerda,direita) e modificar um valor(posição i e j) de uma determinada matriz que esta sendo impressa no terminal (famoso jogo Snake), sem que o "cout<<" seja interrompido (no LINUX) - Estou usando função getch() para ler o buffer do teclado e minha matriz está a todo momento sendo impressa dentro de um for, modificando a posição em que o x aparece assim preenchendo o restante de espaço vazio (" "), alguma dica?


  


2. Re: Como ler dados do teclado sem interromper execução do programa ?

3. Re: Como ler dados do teclado sem interromper execução do programa ?

Lucas Rodrigo Bento Souza
luccasrodge

(usa Ubuntu)

Enviado em 07/12/2014 - 09:55h

Então, isso já consegui fazer, a questão é como fazer a interação dele com a matriz que será impressa ao mesmo tempo que lê as entradas do teclado ...


4. Re: Como ler dados do teclado sem interromper execução do programa ?

Paulo
paulo1205

(usa Ubuntu)

Enviado em 07/12/2014 - 13:18h

É sempre mais fácil ajudar quando você também ajuda. Você poderia ter dito qual compilador está usando, em qual S.O., e, de preferência, mostrando pelo menos uma parte relevante do código fonte. Sem isso, as respostas que podemos dar serão genéricas, especulativas ou até irrelevantes.

Puxando fiapos de informação do seu texto, eu chuto que você está usando o Turbo C++ com ConIO, e que já está chamando kbhit() antes de getch(). Se realmente for o caso, teclas especiais emitem dois bytes de uma vez para o programa, sendo o primeiro byte nulo e o segundo igual ao código de varredura da tecla. Ou seja, se, após o kbhit() ser positivo, getch() retornar 0, você tem de chamar getch() novamente para pegar o código de varredura.


5. Re: Como ler dados do teclado sem interromper execução do programa ?

Lucas Rodrigo Bento Souza
luccasrodge

(usa Ubuntu)

Enviado em 07/12/2014 - 13:26h

Estou usando terminal do Linux em C++ // IDE(QTCreator)

#include <iostream>
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include "matriz.h"
#include <cstdlib>


using namespace std;

int getch()
{
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;
}
int main(){

int l=25;
int c=25;
Matriz* mat = new Matriz(l,c);

string player = "" , foot = "o";

for(int i=0;i<l;i++){
for (int ji=0; ji <c;ji++){
for(int j=2;j<c;j++){
mat->setElemento(i,j,"");

if(i==2 ){

mat->setElemento(i,j,".");

}
}

mat->setElemento(ji,0,".");
}
}

mat->setElemento(3,5,foot);

for(int i=3;i<l;i++){
for(int j=1;j<c;j++){
if(j == 24){
j = 0;

}

mat->setElemento(i,j,"x");

sleep(1);
player = mat->getElemento(i,j);
cout<<mat->getMatriz();
mat->setElemento(i,j,"");
}
}



/* char ch; // Essa parte que desejo fazer interação com minha matriz, conforme eu pressiono //alguma das teclas direcionais modificar a posição da matriz, mais de que forma não interrompa a //execução da matriz se nenhuma tecla for pressionada

while (1)
{
ch = getch();

if (ch == 125)
printf ("\nYou pressed SHIFT+]");

else if (ch == 27)
{
ch = getch();
if (ch == 91)
{
ch = getch();
if (ch == 65){
cout<<"\nCima"<<endl;

}

else if (ch == 66)
cout<<"\nBaixo";
else if (ch == 67)
cout<<"\nDireita";
else if (ch == 68)
cout<<"\nEsquerda";
}
}
}*/

}



6. Re: Como ler dados do teclado sem interromper execução do programa ?

euteste da silva
foxbit3r

(usa Solaris)

Enviado em 07/12/2014 - 15:37h

Para você usar as teclas (cima,baixo,esquerda,direita), você pode utilizar o ncurses.

Um exemplo:


...
case KEY_UP:
if(highlight == 1)
highlight = n_opcoes;
else
--highlight;
break;
case KEY_DOWN:
if(highlight == n_opcoes)
highlight = 1;
else
++highlight;
break;


Codigo completo: http://www.vivaolinux.com.br/script/Cadastro-de-arquivo-usando-ncurses-como-menu/


7. Re: Como ler dados do teclado sem interromper execução do programa ?

Lucas Rodrigo Bento Souza
luccasrodge

(usa Ubuntu)

Enviado em 07/12/2014 - 16:14h

foxbit3r escreveu:

Para você usar as teclas (cima,baixo,esquerda,direita), você pode utilizar o ncurses.

Um exemplo:


...
case KEY_UP:
if(highlight == 1)
highlight = n_opcoes;
else
--highlight;
break;
case KEY_DOWN:
if(highlight == n_opcoes)
highlight = 1;
else
++highlight;
break;


Codigo completo: http://www.vivaolinux.com.br/script/Cadastro-de-arquivo-usando-ncurses-como-menu/


Obrigado pela disposição gente! Agora a dúvida seria outra, o tempo de resposta do teclado tá demorando alguns segundos pra modificar a posição i ou j, já tentei configurar o sleep, só que não to conseguindo, ou seja, uando apertar a tecla (cima,baix,esquerda,direita) mudar exatamente no exato momento a posição ... será necessário abrir outro tópico ? // Segue código abaixo ...

#include <fcntl.h>
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <iostream>
#include "matriz.h"

/* Reads a character directly from the console without buffer, and without echo. */
int getch()
{
struct termios oldattr, newattr;

int ch;
tcgetattr( STDIN_FILENO, &oldattr );
newattr = oldattr;
newattr.c_lflag &= ~( ICANON | ECHO );
tcsetattr( STDIN_FILENO, TCSANOW, &newattr );
ch = getchar();
tcsetattr( STDIN_FILENO, TCSANOW, &oldattr );
return ch;
}

/* Reads a character directly from the console without buffer, but with echo */
int getche()
{
struct termios oldattr, newattr;
int ch;
tcgetattr( STDIN_FILENO, &oldattr );
newattr = oldattr;
newattr.c_lflag &= ~( ICANON );
tcsetattr( STDIN_FILENO, TCSANOW, &newattr );
ch = getchar();
tcsetattr( STDIN_FILENO, TCSANOW, &oldattr );
return ch;
}

/* Determines if a keyboard key was pressed */
int kbhit()
{
struct termios oldt, newt;
int ch;
int oldf;

tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);

ch = getchar();

tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
fcntl(STDIN_FILENO, F_SETFL, oldf);

if(ch != EOF)
{
ungetc(ch, stdin);
return 1;
}
return 0;
}

/* Clears data from the console screen */
void clrscr()
{
char str[] = " [H [2J";
str[3] = str[0] = 27;
write(1, str, 7);
}// http://programmingnotes.freeweq.com/

using namespace std;

int main(){


int l=25;
int c=25;
Matriz* mat = new Matriz(l,c);

string player = "" , foot = "o";

mat->setElemento(3,5,foot);

int r = 0;
int s = 0;
int t = 0;
int u = 0;

for(int i=3;i<l;){

for(int j=1;j<c;){
if(j == 24){
j = 0;
}
sleep(1);

if(kbhit()){
char ch;
ch = getch();
if (ch == 65){
i--;//Cima
r = 1;

s = 0;
t = 0;
u = 0;
}
if (ch == 66){
i++;//Baixo
s = 1;

r = 0;
t = 0;
u = 0;
}
if (ch == 67){
j++;//Direita
t = 1;

r = 0;
s = 0;
u = 0;
}
if (ch == 68){
j--;//Esquerda
u = 1;

r = 0;
s = 0;
t = 0;
}
}



if(r == 1 && s == 0 && t == 0 && u == 0){
i--;//Cima
}
if(s == 1 && r == 0 && t == 0 && u == 0){
i++;//Baixo
}
if(t == 1 && r == 0 && s == 0 && u == 0){
j++;//Direita
}
if(u == 1 && r == 0 && s == 0 && t == 0){
j--;//Esquerda
}

if((i == 3) && (j == 5)){
mat->setElemento(2,4,"o");
}
if(r == 0 && s == 0 && t == 0 && u == 0){
j++; // Andar Inicial
}
mat->setElemento(i,j,"x");

cout<<mat->getMatriz();

player = mat->getElemento(i,j);
mat->setElemento(i,j,"");

}
}




}





8. Re: Como ler dados do teclado sem interromper execução do programa ?

Paulo
paulo1205

(usa Ubuntu)

Enviado em 08/12/2014 - 06:11h

luccasrodge escreveu:

foxbit3r escreveu:

Para você usar as teclas (cima,baixo,esquerda,direita), você pode utilizar o ncurses.

[ ... ]

Codigo completo: http://www.vivaolinux.com.br/script/Cadastro-de-arquivo-usando-ncurses-como-menu/


Obrigado pela disposição gente! Agora a dúvida seria outra, o tempo de resposta do teclado tá demorando alguns segundos pra modificar a posição i ou j, já tentei configurar o sleep, só que não to conseguindo, ou seja, uando apertar a tecla (cima,baix,esquerda,direita) mudar exatamente no exato momento a posição ... será necessário abrir outro tópico ?


Eu quase acertei quando falei em ConIO, não? Eu errei ao supor o Turbo C sobre DOS/Windows, mas o que você fez foi reinventar a ConIO do Turbo C, ou um pedaço dela, no Linux.

Você deveria considerar as boas dicas que lhe foram dadas. Pode acreditar: Curses é muito melhor do que ConIO. Além disso, a não ser que seja com objetivo didático, reinventar a roda costuma ser meio contraproducente.

No entanto, permita-me ajudá-lo com a “neorroda”.

As operações com terminal são necessariamente de baixo nível. Assim sendo, a primeira dica que lhe dou é não misturar operações sobre o descritor STDIO_FILENO com entrada de mais alto nível no stream stdin. Trabalhe consistentemente somente com o descritor, até porque você não conhece nem tem muito controle sobre o funcionamento interno das operações feitas sobre streams (aliás, os comentários no seu código que dizem fazer uma leitura sem buffers estão simplesmente errados).

Em lugar das chamadas a getchar() dentro da sua versão de getch() e getche(), use algo como vai abaixo.

char ch;
if(read(STDIN_FILENO, &ch, 1)==1){
/* Leitura bem sucedida. */
}


Sua função kbhit() tem problema parecido. Na verdade, tudo o que você gostaria de fazer é um select() ou poll() aplicado a STDIN_FILENO.

inline bool kbhit(){
struct pollfd pollstdin={STDIN_FILENO, POLLIN, 0};
return poll(&pollstdin, 1, 0)==1;
}


Nessas três funções, é um desperdício tirar o terminal de modo canônico e desligar o eco e religar depois, pois você não faz qualquer operação de entrada que dependa do modo canônico voltar a ser ligado (aliás, nem do eco; sua função getche() não é usada ao longo do programa -- em todo caso é trivialmente fácil sintetizar uma getche() que não precise ligar o eco, a partir de getch() e de uma operação de saída no terminal do byte por ela lido, se tal byte for imprimível). Parece-me que você quer que o modo canônico e o eco permaneçam desligados durante todo o jogo. Então faça isso só uma vez, no começo do programa, lembrando apenas de religá-los antes de o programa terminar.

Você não a usa, apesar de a definir, mas a função clrscr() tem uma estranha dupla passada para definir um string com uma sequência de escape. Tal dupla passada é desnecessária, pois o C e o C++ permitem embutir qualquer caráter no meio de um string através das notações “\número_octal“ ou “\xnúmero_hexadecimal”. Você poderia, portanto, escrever "\33[H\33[2J" ou "\x1b[H\x1b[2J". Em todo caso, a melhor forma de fazer o que você quer não é usar uma sequência de escape fixa de um tipo específico de terminal. Ainda que esse tipo seja, na prática, o mais comum, você pode topar com gente como eu, que passou anos trabalhando com terminais de texto que não eram compatíveis com VT100 nem ANSI (e aguarda a chance de ter um daqueles velhos terminais na sua coleção). Além de para esses dinossauros, para qualquer pessoa minimamente interessada em fazer código portável a forma correta de limpar a tela é consultar as informações do terminal e usar a sequência de escape correta para tal terminal.

/* No começo do programa... */
setupterm(NULL, STDOUT_FILENO, NULL);
/*
O NULL no primeiro argumento faz com que o terminal seja lido
da variável de ambiente TERM. O do terceiro é preguiça de alocar
uma variável para receber eventuais sinalizações de erro. Fique
à vontade para melhorar isso.
*/

/* ... */

/* Para limpar a tela. */
putp(clear_screen);


(Para usar isso, na hora de chamar o linker, inclua a biblioteca termcap (opção “-ltermcap” passada ao gcc ou ao ld). Leia a documentação de setupterm() e putp() -- é a mesma manpage, aliás.)

Não entendi o problema com sleep() que você mencionou. Em todo caso, acho que, para um jogo de testes de habilidades, uma pausa mínima de um segundo é um tanto grande demais. Considere usar usleep() ou nanosleep(), ou aproveite os parâmetros de timeout de poll() (em milissegundos) ou select() (resolução de microssegundos). Outra opção é usar um timer para redesenhar a tela periodicamente (leia sobre setitimer() e sigaction()).






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts