Skip to content

Input System

The input layer translates raw MLX events into persistent runtime intent stored in app->input. Event callbacks only update state; the frame loop applies movement and rotation once per render tick.

Input pipeline

Hooks write intent, the frame loop consumes it

`cub3D` does not move the player directly from event callbacks. MLX hooks update `app->input`, then `draw_frame()` consumes that state once per frame for stable movement and rotation.

01
main() binds MLX hooks

One-time setup before `mlx_loop`, not part of the hot path.

key press key release mouse move mouse button destroy window
02
handlers mutate `app->input`

Callbacks store intent only, so movement remains frame-rate aware.

key_press

sets explicit movement or turn flags in `app->input`

input.forward = 1
key_release

clears the same explicit flags when the OS reports release

input.forward = 0
direct action keys

trigger cleanup, door toggle, resize, level, or weapon actions immediately

ESC / E / Space / F1-F4
mouse_move

accumulates horizontal delta for bonus camera rotation

input.mouse_dx += delta_x
destroy / quit

routes direct exit actions through cleanup

close_window(app)
03
interactive input state

Press keys to see how persistent flags and movement compose.

Current flags `t_input = `
04
`update_player_input(app)` once per frame

`draw_frame()` consumes input in a deterministic order.

  1. 01 read `t_input` flags read
  2. 02 compute move amount with `delta_time` delta
  3. 03 run collision-safe movement collision
  4. 04 apply keyboard + mouse rotation rotate
  5. 05 reset one-frame mouse delta reset
`raycast_scene()` sees coherent camera state

Position, direction vector, and camera plane are updated before the current frame is rendered.

Hooks are bound in main before entering mlx_loop.

Typical bindings cover:

  • key press
  • key release
  • mouse button press
  • mouse motion
  • window destroy

These handlers either mutate app->input or trigger direct actions such as close_window(...).

The project uses a state-based keyboard model:

  • press sets a flag
  • release clears the flag
  • continuous movement comes from flags read every frame

Those flags are explicit fields such as forward, backward, left, right, turn_left, and turn_right; the project does not use a generic keys[] table.

This avoids missing movement when event timing and frame timing differ.

In bonus mode, mouse movement contributes to horizontal look by accumulating relative delta in the input state. That value is then consumed during update_player_input(...) and reset after the frame update.

Mouse buttons and wheel events are used for bonus actions such as firing and minimap zoom, depending on the active build. Other direct action keys, such as ESC, E, Space, and F1-F4, trigger their action in the event handler instead of becoming persistent movement flags.

draw_frame(...) calls update_player_input(...) once per frame.

The current flow is:

  1. read pressed flags from t_input
  2. compute movement from delta_time
  3. apply collision-safe displacement
  4. rotate camera with keyboard and optional mouse delta
  5. reset one-frame mouse contribution

This keeps controls stable and frame-rate aware.

Primary ownership stays inside t_app:

  • app->input stores persistent key and mouse state
  • movement updates mutate player position
  • rotation updates mutate dir and plane

The input hot path does not allocate heap memory. Unknown keys are ignored safely, and quit actions still route through the normal cleanup path.

  • srcs/core/main.c
  • srcs/input/input_keys.c
  • srcs/input/input_mouse.c
  • srcs/input/input_update.c
  • srcs/input/move_player.c
  • srcs/input/move_collision.c
  • srcs/input/rotation.c