Divida isso em várias camadas.
Na camada mais baixa, você tem eventos de entrada brutos do sistema operacional. Entrada de teclado SDL, entrada de mouse, entrada de joystick, etc. Você pode ter várias plataformas (SDL é um denominador menos comum, sem várias formas de entrada, por exemplo, com as quais você pode se interessar mais tarde).
Você pode abstraí-los com um tipo de evento personalizado de nível muito baixo, como "botão do teclado pressionado" ou algo semelhante. Quando sua camada de plataforma (loop de jogo SDL) recebe entrada, ela deve criar esses eventos de baixo nível e encaminhá-los para um gerenciador de entrada. Isso pode ser feito com chamadas de método simples, funções de retorno de chamada, um sistema de eventos complicado, o que você mais gosta.
O sistema de entrada agora tem a tarefa de converter entradas de baixo nível em eventos lógicos de alto nível. A lógica do jogo nem liga para o fato de o ESPAÇO ter sido pressionado. Importa-se que JUMP tenha sido pressionado. O trabalho do gerente de entrada é coletar esses eventos de baixo nível e gerar eventos de alto nível. É responsável por saber que a barra de espaço e o botão 'A' do gamepad são mapeados para o comando lógico Jump. Ele lida com controles de aparência de gamepad e mouse e assim por diante. Ele emite eventos lógicos de alto nível os mais abstratos possíveis dos controles de baixo nível (existem algumas limitações aqui, mas você pode abstrair completamente as coisas no caso comum).
Seu controlador de personagem recebe esses eventos e processa esses eventos de entrada de alto nível para realmente responder. A camada da plataforma enviou o evento "Barra de espaço para baixo". O sistema de entrada recebeu isso, analisa suas tabelas de mapeamento / lógica e envia o evento "Salto pressionado". O controlador de lógica / personagem do jogo recebe esse evento, verifica se o jogador está realmente autorizado a pular e, em seguida, emite o evento "O jogador pulou" (ou apenas provoca um salto diretamente), que o restante da lógica do jogo usa para fazer o que quer .
Qualquer coisa dependente da lógica do jogo entra no controle do jogador. Qualquer coisa dependente do SO entra na camada da plataforma. Todo o resto vai para a camada de gerenciamento de entrada.
Aqui está uma arte ASCII amadora para descrever isso:
-----------------------------------------------------------------------
Platform Abstraction | Collect and forward OS input events
-----------------------------------------------------------------------
| |
| |
\ /
\_/
-----------------------------------------------------------------------
Input Manager | Translate OS input events into logical events
-----------------------------------------------------------------------
| |
| |
\ /
\_/
-----------------------------------------------------------------------
Character Controller | React to logical events and affect game play
-----------------------------------------------------------------------
| |
| |
\ /
\_/
-----------------------------------------------------------------------
Game Logic | React to player actions and provides feedback
-----------------------------------------------------------------------