Em SDL, quando queremos tratar eventos, usamos uma função parecida com a do trecho abaixo dentro do nosso game loop.

void handleInput() {
    SDL_Event event;
    while (SDL_PollEvent(&event)) {
        switch (event.type) {
            case SDL_QUIT:
                quit = SDL_TRUE;
                break;
            case SDL_KEYDOWN:
                // do something
                break;
            case SDL_KEYUP:
                // do something
                break;
        }
    }
}

Mas há momentos em que queremos tratar certos eventos (no caso, eventos de teclado) fora dessa função, e para esses momentos o SDL nos fornece a SDL_GetKeyboardState(), que podemos “embrulhar” numa outra função pra ficar mais amigável.

// A tecla está pressionada?
SDL_bool isKeyPressed(int key) {
    return SDL_GetKeyboardState(NULL) [key];
}

Um exemplo simples seria fazer o personagem pular ao pressionarmos a tecla de espaço.

if ( isKeyPressed(SDL_SCANCODE_SPACE) )
    player.y -= JUMP_FORCE;

O problema com o trecho acima é que, enquanto a tecla de espaço estiver pressionada, nosso personagem continuará a subir indefinidamente.

O comportamento que desejamos aqui é que, ao pressionarmos “espaço”, o personagem realize o pulo e o encerre normalmente e só seja capaz de pular novamente caso paremos de apertar e a apertemos novamente.

A solução que encontrei foi a seguinte:

// Variável global
Uint8 keyStates[SDL_NUM_SCANCODES];

// Reinicia estados
void resetKeyStates() {
    for (size_t i = 0; i < SDL_NUM_SCANCODES; i++)
        keyStates[i] = 0;
}

// A tecla foi pressinada?
Uint8 wasKeyPressed(int key) {
    return keyStates[key] == 1;
}

// A tecla foi liberada?
Uint8 wasKeyReleased(int key) {
    return keyStates[key] == 2;
}

O próximo passo é modificar um pouco o nosso loop de eventos:

void handleInput() {
    resetKeyStates();
    SDL_Event event;
    while (SDL_PollEvent(&event)) {
        switch (event.type) {
            case SDL_QUIT:
                quit = SDL_TRUE;
                break;
            case SDL_KEYDOWN:
                keyStates[event.key.keysym.scancode] = !event.key.repeat;
                break;
            case SDL_KEYUP:
                keyStates[event.key.keysym.scancode] = 2;
                break;
        }
    }
}

E pronto. Faça o teste com isso:

int main(int argc, char *argv[]) {
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window* window = SDL_CreateWindow("Window Title", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, 0);

    while ( !quit ) {
        handleInput();

        if ( isKeyPressed(SDL_SCANCODE_A) )
            SDL_Log("A is pressed!");

        if ( wasKeyPressed(SDL_SCANCODE_SPACE) )
            SDL_Log("Space was pressed!");

        if ( wasKeyReleased(SDL_SCANCODE_SPACE) )
            SDL_Log("Space was released!");
    }

    SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;
}

O código de teste acima está completo aqui: test.c

E aqui tem um exemplo mais visual: game.c