#include "engine/input.h" #include static bool s_current[ACTION_COUNT]; static bool s_previous[ACTION_COUNT]; /* * Latched states: accumulate press/release events across frames * so that a press is never lost even if no update tick runs * during the frame it was detected. */ static bool s_latched_pressed[ACTION_COUNT]; static bool s_latched_released[ACTION_COUNT]; static bool s_quit_requested; /* ── Mouse state ──────────────────────────────────── */ static int s_mouse_x, s_mouse_y; static bool s_mouse_current[MOUSE_BUTTON_COUNT]; static bool s_mouse_previous[MOUSE_BUTTON_COUNT]; static bool s_mouse_latched_pressed[MOUSE_BUTTON_COUNT]; static bool s_mouse_latched_released[MOUSE_BUTTON_COUNT]; static int s_mouse_scroll; /* ── Raw keyboard state ───────────────────────────── */ static const Uint8 *s_key_state = NULL; static Uint8 s_prev_keys[SDL_NUM_SCANCODES]; static Uint8 s_latched_keys[SDL_NUM_SCANCODES]; /* Default key bindings (primary + alternate) */ static SDL_Scancode s_bindings[ACTION_COUNT] = { [ACTION_LEFT] = SDL_SCANCODE_LEFT, [ACTION_RIGHT] = SDL_SCANCODE_RIGHT, [ACTION_UP] = SDL_SCANCODE_UP, [ACTION_DOWN] = SDL_SCANCODE_DOWN, [ACTION_JUMP] = SDL_SCANCODE_Z, [ACTION_SHOOT] = SDL_SCANCODE_X, [ACTION_DASH] = SDL_SCANCODE_C, [ACTION_PAUSE] = SDL_SCANCODE_ESCAPE, }; /* Alternate bindings (0 = no alternate) */ static SDL_Scancode s_alt_bindings[ACTION_COUNT] = { [ACTION_SHOOT] = SDL_SCANCODE_SPACE, }; void input_init(void) { memset(s_current, 0, sizeof(s_current)); memset(s_previous, 0, sizeof(s_previous)); memset(s_latched_pressed, 0, sizeof(s_latched_pressed)); memset(s_latched_released, 0, sizeof(s_latched_released)); memset(s_mouse_current, 0, sizeof(s_mouse_current)); memset(s_mouse_previous, 0, sizeof(s_mouse_previous)); memset(s_mouse_latched_pressed, 0, sizeof(s_mouse_latched_pressed)); memset(s_mouse_latched_released, 0, sizeof(s_mouse_latched_released)); memset(s_prev_keys, 0, sizeof(s_prev_keys)); memset(s_latched_keys, 0, sizeof(s_latched_keys)); s_mouse_x = s_mouse_y = 0; s_mouse_scroll = 0; s_quit_requested = false; } void input_poll(void) { /* Save previous state */ memcpy(s_previous, s_current, sizeof(s_current)); memcpy(s_mouse_previous, s_mouse_current, sizeof(s_mouse_current)); s_quit_requested = false; s_mouse_scroll = 0; /* Save previous raw key state */ if (s_key_state) { memcpy(s_prev_keys, s_key_state, SDL_NUM_SCANCODES); } /* Process SDL events */ SDL_Event event; while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: s_quit_requested = true; break; case SDL_MOUSEWHEEL: s_mouse_scroll += event.wheel.y; break; } } /* Read keyboard state */ s_key_state = SDL_GetKeyboardState(NULL); for (int i = 0; i < ACTION_COUNT; i++) { s_current[i] = s_key_state[s_bindings[i]]; if (s_alt_bindings[i] && s_key_state[s_alt_bindings[i]]) { s_current[i] = true; } /* Latch edges: once set, stays true until consumed */ if (s_current[i] && !s_previous[i]) { s_latched_pressed[i] = true; } if (!s_current[i] && s_previous[i]) { s_latched_released[i] = true; } } /* Latch raw key edges */ if (s_key_state) { for (int i = 0; i < SDL_NUM_SCANCODES; i++) { if (s_key_state[i] && !s_prev_keys[i]) { s_latched_keys[i] = 1; } } } /* Read mouse state */ Uint32 buttons = SDL_GetMouseState(&s_mouse_x, &s_mouse_y); /* Convert window coords to logical coords (SDL handles this via SDL_RenderSetLogicalSize, but GetMouseState returns window coords) */ SDL_Renderer *r = SDL_GetRenderer(SDL_GetMouseFocus()); if (r) { float lx, ly; SDL_RenderWindowToLogical(r, s_mouse_x, s_mouse_y, &lx, &ly); s_mouse_x = (int)lx; s_mouse_y = (int)ly; } s_mouse_current[MOUSE_LEFT] = (buttons & SDL_BUTTON_LMASK) != 0; s_mouse_current[MOUSE_MIDDLE] = (buttons & SDL_BUTTON_MMASK) != 0; s_mouse_current[MOUSE_RIGHT] = (buttons & SDL_BUTTON_RMASK) != 0; for (int i = 0; i < MOUSE_BUTTON_COUNT; i++) { if (s_mouse_current[i] && !s_mouse_previous[i]) { s_mouse_latched_pressed[i] = true; } if (!s_mouse_current[i] && s_mouse_previous[i]) { s_mouse_latched_released[i] = true; } } } void input_consume(void) { /* Clear latched states after an update tick has read them */ memset(s_latched_pressed, 0, sizeof(s_latched_pressed)); memset(s_latched_released, 0, sizeof(s_latched_released)); memset(s_mouse_latched_pressed, 0, sizeof(s_mouse_latched_pressed)); memset(s_mouse_latched_released, 0, sizeof(s_mouse_latched_released)); memset(s_latched_keys, 0, sizeof(s_latched_keys)); } bool input_pressed(Action a) { return s_latched_pressed[a]; } bool input_held(Action a) { return s_current[a]; } bool input_released(Action a) { return s_latched_released[a]; } bool input_quit_requested(void) { return s_quit_requested; } /* ── Mouse queries ────────────────────────────────── */ void input_mouse_pos(int *x, int *y) { if (x) *x = s_mouse_x; if (y) *y = s_mouse_y; } bool input_mouse_pressed(MouseButton btn) { if (btn < 0 || btn >= MOUSE_BUTTON_COUNT) return false; return s_mouse_latched_pressed[btn]; } bool input_mouse_held(MouseButton btn) { if (btn < 0 || btn >= MOUSE_BUTTON_COUNT) return false; return s_mouse_current[btn]; } bool input_mouse_released(MouseButton btn) { if (btn < 0 || btn >= MOUSE_BUTTON_COUNT) return false; return s_mouse_latched_released[btn]; } int input_mouse_scroll(void) { return s_mouse_scroll; } /* ── Raw keyboard queries ─────────────────────────── */ bool input_key_pressed(SDL_Scancode key) { if (key < 0 || key >= SDL_NUM_SCANCODES) return false; return s_latched_keys[key] != 0; } bool input_key_held(SDL_Scancode key) { if (key < 0 || key >= SDL_NUM_SCANCODES) return false; return s_key_state && s_key_state[key]; } void input_shutdown(void) { /* Nothing to clean up */ }