Usando o Teclado

Estruturas Relacionadas ao Teclado

Deverá ser bem mais fácil entender este tutorial se você estiver familiarizado com os tipos de dados envolvidos no acesso ao teclado, então eu os explicarei primeiro.

SDLKey

SDLKey é um tipo enumerado definido em SDL/include/SDL_keysym.h e detalhado aqui. Cada símbolo de SDLKey representa uma tecla, SDLK_a corresponde a tecla 'a' no teclado, SDLK_SPACE corresponde a barra de espaço, e assim por diante.

SDLMod

SDLMod é um tipo enumerado, similar ao SDLKey, mas ele enumera teclas modificadoras (Control, Alt, Shift). A lista completa dos modificadores está aqui. Os valores de SDLMod juntos podem E PODERIAM representar vários modificadores.

SDL_keysym

typedef struct{

  Uint8 scancode;
  SDLKey sym;
  SDLMod mod;
  Uint16 unicode;

} SDL_keysym;

A estrutura SDL_keysym descreve uma tecla pressionada ou uma tecla liberada. O campo scancode é específico ao hardware e deve ser ignorado ao menos que você saiba o que está fazendo. O campo sym é o valor de SDLKey da tecla pressionada ou liberada. O campo mod descreve o estado dos modificadores de teclado no momento em que a tecla foi pressionada ou liberada. Então, um valor de KMOD_NUM | KMOD_CAPS | KMOD_LSHIFT significariam que as teclas Numlock, Capslock e o Shift esquerdo estão todas pressionadas (ou habilitadas no caso das teclas de travamento). Finalmente, o campo unicode armazena o valor de 16-bit unicode da tecla.

Nota: Deve ser observado e entendido que este campo é valido somente quando o SDL_keysym está descrevendo uma tecla pressionada, não uma tecla liberada. Valores unicodes apenas percebem teclas pressionadas porque o valor unicode descreve um caractere internacional e somente teclas pressionadas produzem caracteres. Mais informações podem ser encontradas em www.unicode.org

Nota: A conversão unicode deve ser habilitada usando a função SDL_EnableUNICODE

SDL_KeyboardEvent

typedef struct{

  Uint8 type;
  Uint8 state;
  SDL_keysym keysym;
} SDL_KeyboardEvent;

A SDL_KeyboardEvent descreve um evento de teclado (obviamente). O membro key da união SDL_Event é uma estrutura SDL_KeyboardEvent. O campo type especifica se o evento é um evento de tecla liberada (SDL_KEYUP) ou tecla pressionada (SDL_KEYDOWN). O state é totalmente redundante, ele reporta a mesma informação do campo type, mas, usa valores diferentes (SDL_RELEASED e SDL_PRESSED). O keysym contém informações sobre a tecla pressionada ou liberada que este evento representa. (veja acima).

Lendo Eventos do Teclado

Ler a pilha de eventos do teclado é realmente simples (a pilha de eventos, e como usá-la está descrito aqui). Nós lemos os eventos usando SDL_PollEvent em um loop while() e checando pelos eventos SDL_KEYUP e SDL_KEYDOWN usando uma declaração switch, como esta:

Exemplo 3-10. Lendo Eventos do Teclado

  SDL_Event event;
  .
  .
  /* Pesquisa por eventos. SDL_PollEvent() retorna 0 quando não há mais */
  /* eventos na pilha de eventos, nosso loop while irá terminar quando */
  /* isto acontecer.                                                  */
  while( SDL_PollEvent( &event ) ){
    /* Nós estamos interessados nos eventos SDL_KEYDOWN e SDL_KEYUP */
    switch( event.type ){

      case SDL_KEYDOWN:
        printf( "Tecla pressionada\n" );
        break;

      case SDL_KEYUP:
        printf( "Tecla liberada\n" );
        break;

      default:
        break;
    }
  }
  .
  .

Este é um exemplo muito simples. Nenhuma informação sobre as tecla pressionada ou liberada é interpretada. Nós iremos explorar os outros extremos em nosso primeiro exemplo completo abaixo – reportando toda informação disponível sobre um evento de teclado.

Uma Olhada Mais Detalhada

Antes de podermos ler eventos, a SDL deve ser iniciada com SDL_Init e um modo de vídeo deve ser estabelecido usando SDL_SetVideoMode. Existem também duas outras funções que vamos usar para obter todas as informações requeridas. Vamos habilitar a conversão unicode chamando SDL_EnableUNICODE(1) e devemos converter valores SDLKey em algo imprimível, usando SDL_GetKeyName

Nota: É útil notar que valores unicodes < que 0x80 convertem diretamente caracteres de valor ASCII. Isto é usado no exemplo abaixo

Exemplo 3-11. Interpretando Informações de Eventos de Teclado

    #include "SDL.h"

    /* Protótipos de função */
    void PrintKeyInfo( SDL_KeyboardEvent *key );
    void PrintModifiers( SDLMod mod );
 
    /* função main */
    int main( int argc, char *argv[] ){

        SDL_Event event;
        int quit = 0;

        /* Inicia a SDL */
        if( SDL_Init( SDL_INIT_VIDEO ) ){
            fprintf( stderr, "Não foi possível iniciar SDL: %s\n", SDL_GetError() );
            exit( -1 );
        }

        /* Estabelecendo um modo de vídeo */
        if( !SDL_SetVideoMode( 320, 200, 0, 0 ) ){
            fprintf( stderr, "Não foi possível estabelecer um modo de vídeo: %s\n", SDL_GetError() );

            SDL_Quit();
            exit( -1 );
        }

        /* Habilita conversão unicode */
        SDL_EnableUNICODE( 1 );

        /* Laço rodando até que um evento SDL_QUIT ocorra */
        while( !quit ){

            /* Consulta de eventos */
            while( SDL_PollEvent( &event ) ){
 
               switch( event.type ){
                    /* Eventos de teclado */
                    /* Passa os dados do evento para PrintKeyInfo() */
                    case SDL_KEYDOWN:

                    case SDL_KEYUP:
                        PrintKeyInfo( &event.key );
                        break;

                    /* Evento SDL_QUIT (window close) */
                    case SDL_QUIT:
                        quit = 1;
                        break;

                    default:
                        break;
                }
            }
        }

        /* Limpa tudo */
        SDL_Quit();
        exit( 0 );
    }

    /* Imprime toda a informação sobre um evento de tecla */
    void PrintKeyInfo( SDL_KeyboardEvent *key ){

        /* Está sendo liberado ou pressionado? */
        if( key->type == SDL_KEYUP )
            printf( "Liberada:- " );
        else
            printf( "Pressionada:- " );

        /* Imprime o scancode do hardware primeiro */
        printf( "Scancode: 0x%02X", key->keysym.scancode );

        /* Imprime o nome da tecla */
        printf( ", Nome: %s", SDL_GetKeyName( key->keysym.sym ) );

        /* Nós queremos imprimir a informação unicode, mas nós precisamos */
        /* ter certeza que isso é um evento de tecla pressionada (lembre-se, */
        /* eventos de teclas liberadas não possuem informação unicode) */

        if( key->type == SDL_KEYDOWN ){
            /* Se o valor unicode for menor que 0x80 então o valor  */
            /* unicode pode ser usado para pegar uma  representação */
            /* imprimível da tecla, usando (char)unicode  */

            printf(", Unicode: " );

            if( key->keysym.unicode < 0x80 && key->keysym.unicode > 0 ){
                printf( "%c (0x%04X)", (char)key->keysym.unicode,
                        key->keysym.unicode );
            }
            else{
                printf( "? (0x%04X)", key->keysym.unicode );
            }
        }
        printf( "\n" );

        /* Imprime as informações dos modificadores */
        PrintModifiers( key->keysym.mod );
    }

    /* Imprime informações dos modificadores */
    void PrintModifiers( SDLMod mod ){
        printf( "Modificadores: " );

        /* Se não existirem então diz isso e retorna */
        if( mod == KMOD_NONE ){
            printf( "Nenhum\n" );
            return;
        }

        /* Checa a presença de cada valor de SDLMod */
        /* Isto parece sujo, mas realmente não existe */
        /* uma maneira limpa.                        */

        if( mod & KMOD_NUM ) printf( "NUMLOCK " );
        if( mod & KMOD_CAPS ) printf( "CAPSLOCK " );
        if( mod & KMOD_LCTRL ) printf( "LCTRL " );
        if( mod & KMOD_RCTRL ) printf( "RCTRL " );
        if( mod & KMOD_RSHIFT ) printf( "RSHIFT " );
        if( mod & KMOD_LSHIFT ) printf( "LSHIFT " );
        if( mod & KMOD_RALT ) printf( "RALT " );
        if( mod & KMOD_LALT ) printf( "LALT " );
        if( mod & KMOD_CTRL ) printf( "CTRL " );
        if( mod & KMOD_SHIFT ) printf( "SHIFT " );
        if( mod & KMOD_ALT ) printf( "ALT " );

        printf( "\n" );
    }

Entrada Para Jogos

Eu tenho encontrado pessoas usando eventos de teclado para jogos e outras aplicações interativas muitas vezes não entendendo um ponto fundamental.

Eventos de teclado apenas acontecem quanto o estado de uma tecla muda de não-pressionado para pressionado e vice versa.

Imagine que você possui uma imagem de um alienígena que você deseja mover usando as teclas de cursor – quando você pressiona a tecla da esquerda você quer que ele deslize para a esquerda, quando você pressiona a tecla para baixo você quer que ele deslize para baixo do vídeo. Olhe o código seguinte, ele sinaliza um erro que muitas pessoas cometem.

    /* Coordenadas do alienígena na tela */
    int alien_x=0, alien_y=0;
    .
    .

    /* Inicializa SDL, vídeo e todas aquelas coisas... */
    .
   
    /* Laço principal do jogo */
    /* Checa os eventos */
    while( SDL_PollEvent( &event ) ){

        switch( event.type ){
            /* Procura por um evento de tecla pressionada */
            case SDL_KEYDOWN:

                /* Checa os valores de SDLKey para modificar as coordenadas */
                switch( event.key.keysym.sym ){

                    case SDLK_LEFT:
                        alien_x -= 1;
                        break;

                    case SDLK_RIGHT:
                        alien_x += 1;
                        break;

                    case SDLK_UP:
                        alien_y -= 1;
                        break;

                    case SDLK_DOWN:
                        alien_y += 1;
                        break;

                    default:
                        break;
                }
            }
        }
    }
    .
    .

A principio você pode pensar que isto é o pedaço de código ideal para esta tarefa, mas ele não é. Como eu disse, eventos de teclado apenas ocorrem quando uma tecla muda de estado, então o usuário teria que pressionar e soltar a tecla de cursor para esquerda 100 vezes para mover o alienígena 100 pixels para esquerda.

Para contornar este problema, não devemos usar os eventos para mudar a posição do alienígena, nós usaremos os eventos para definir “flags” que serão usadas em uma seção separada de código para mover o alienígena. Algo como isto:

Exemplo 3-12. Movimento Adequado Para Jogos

    /* Coordenadas do alienígena na tela */

    int alien_x=0, alien_y=0;
    int alien_xvel=0, alien_yvel=0;
    .
    .

    /* Inicializa SDL, vídeo e todas aquelas coisas... */
    .

    /* Laço principal do jogo */
    /* Checa os eventos */

    while( SDL_PollEvent( &event ) ){

        switch( event.type ){
            /* Procura por uma tecla pressionada */
            case SDL_KEYDOWN:

                /* Checa os valores de SDLKey para modificar as coordenadas */
                switch( event.key.keysym.sym ){
                    case SDLK_LEFT:
                        alien_xvel = -1;
                        break;

                    case SDLK_RIGHT:
                        alien_xvel =  1;
                        break;

                    case SDLK_UP:
                        alien_yvel = -1;
                        break;

                    case SDLK_DOWN:
                        alien_yvel =  1;
                        break;

                    default:
                        break;
                }
                break;

            /* Devemos também usar os eventos SDL_KEYUP para zerar as variáveis */
            /* de velocidade x e y. Mas temos que ser cuidadosos para não zerarmos */
            /* velocidades que não deveríamos                                     */        

            case SDL_KEYUP:
                switch( event.key.keysym.sym ){
                    case SDLK_LEFT:

                        /* Vamos checar para ter certeza de que o alienígena */
                        /* está movendo para esquerda. Se estiver então nós  */
                        /* zeramos a velocidade. Se o alienígena estiver se  */
                        /* movendo para direita, então a tecla para direita  */
                        /* continua pressionada, logo, não devemos mexer     */
                        /* nessa velocidade                                  */

                        if( alien_xvel < 0 )
                            alien_xvel = 0;

                        break;

                    case SDLK_RIGHT:
                        if( alien_xvel > 0 )
                            alien_xvel = 0;

                        break;

                    case SDLK_UP:
                        if( alien_yvel < 0 )
                            alien_yvel = 0;

                        break;

                    case SDLK_DOWN:

                        if( alien_yvel > 0 )
                            alien_yvel = 0;

                        break;

                    default:
                        break;
                }
                break;

            default:
                break;
        }
    }
    .
    .

    /* Atualiza as coordenadas do alienígena */

    alien_x += alien_xvel;
    alien_y += alien_yvel;

Como pode ser visto, usamos duas variáveis extras, alien_xvel e alien_yvel, que representam o movimento da nave, são estas variáveis que nós atualizamos quando detectamos teclas pressionadas e liberadas.

SDLDoc-ptBR/Tratando_o_Teclado (last edited 2008-04-17 08:18:51 by localhost)