2026-07-30 01:10:31 -04:00
|
|
|
/**
|
|
|
|
|
* @file controller.h
|
|
|
|
|
* @brief Declares the public controller API.
|
|
|
|
|
*/
|
|
|
|
|
|
2025-08-03 21:42:12 -04:00
|
|
|
#ifndef _CONTROLLER_H_
|
|
|
|
|
#define _CONTROLLER_H_
|
|
|
|
|
|
|
|
|
|
#include <SDL3/SDL.h>
|
2026-01-05 08:58:06 -05:00
|
|
|
#include <akerror.h>
|
2026-05-08 22:01:56 -04:00
|
|
|
#include "types.h"
|
2025-08-03 21:42:12 -04:00
|
|
|
|
2026-05-06 23:18:42 -04:00
|
|
|
#define AKGL_MAX_CONTROL_MAPS 8
|
|
|
|
|
#define AKGL_MAX_CONTROLS 32
|
2025-08-03 21:42:12 -04:00
|
|
|
|
2026-07-31 06:57:50 -04:00
|
|
|
/**
|
|
|
|
|
* @brief Keystrokes akgl_controller_handle_event() will hold for a poller.
|
|
|
|
|
*
|
|
|
|
|
* The Commodore keyboard buffer this serves held ten. Thirty-two is enough that
|
|
|
|
|
* a program which polls once per frame never loses a key to a fast typist, and
|
|
|
|
|
* small enough that the buffer stays a fixed-size object in the library's data
|
|
|
|
|
* segment.
|
|
|
|
|
*/
|
|
|
|
|
#define AKGL_CONTROLLER_KEY_BUFFER 32
|
|
|
|
|
|
2026-07-30 01:10:31 -04:00
|
|
|
/** @brief Maps one SDL input to pressed and released callbacks. */
|
2025-08-03 21:42:12 -04:00
|
|
|
typedef struct {
|
2026-05-08 22:01:56 -04:00
|
|
|
uint32_t event_on;
|
|
|
|
|
uint32_t event_off;
|
|
|
|
|
uint8_t button;
|
2025-08-03 21:42:12 -04:00
|
|
|
SDL_Keycode key;
|
2026-05-08 22:01:56 -04:00
|
|
|
uint8_t axis;
|
|
|
|
|
uint8_t axis_range_min;
|
|
|
|
|
uint8_t axis_range_max;
|
2026-05-06 23:18:42 -04:00
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *(*handler_on)(akgl_Actor *obj, SDL_Event *event);
|
|
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *(*handler_off)(akgl_Actor *obj, SDL_Event *event);
|
|
|
|
|
} akgl_Control;
|
2025-08-03 21:42:12 -04:00
|
|
|
|
2026-07-30 01:10:31 -04:00
|
|
|
/** @brief Groups input bindings for one actor and its input devices. */
|
2025-08-03 21:42:12 -04:00
|
|
|
typedef struct {
|
2026-05-06 23:18:42 -04:00
|
|
|
akgl_Actor *target;
|
2026-05-08 22:01:56 -04:00
|
|
|
uint16_t nextMap;
|
2026-05-06 23:18:42 -04:00
|
|
|
akgl_Control controls[AKGL_MAX_CONTROLS];
|
2025-08-03 21:42:12 -04:00
|
|
|
SDL_KeyboardID kbid;
|
|
|
|
|
SDL_JoystickID jsid;
|
|
|
|
|
SDL_MouseID mouseid;
|
|
|
|
|
SDL_PenID penid;
|
2026-05-06 23:18:42 -04:00
|
|
|
} akgl_ControlMap;
|
2025-08-03 21:42:12 -04:00
|
|
|
|
2026-07-30 01:10:31 -04:00
|
|
|
/** @brief Stores all process-wide actor input maps. */
|
2026-05-06 23:18:42 -04:00
|
|
|
extern akgl_ControlMap GAME_ControlMaps[AKGL_MAX_CONTROL_MAPS];
|
2025-08-03 21:42:12 -04:00
|
|
|
|
2026-07-30 01:10:31 -04:00
|
|
|
/**
|
|
|
|
|
* @brief Controller list keyboards.
|
|
|
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
|
|
|
|
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
|
|
|
|
*/
|
2026-05-13 08:02:59 -04:00
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_list_keyboards(void);
|
|
|
|
|
|
2026-07-30 01:10:31 -04:00
|
|
|
/**
|
|
|
|
|
* @brief Controller handle event.
|
|
|
|
|
* @param appstate Application state supplied by SDL.
|
|
|
|
|
* @param event SDL input event to process.
|
|
|
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
|
|
|
|
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
|
|
|
|
*/
|
2026-05-06 23:18:42 -04:00
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_handle_event(void *appstate, SDL_Event *event);
|
2025-08-03 21:42:12 -04:00
|
|
|
|
2026-07-30 01:10:31 -04:00
|
|
|
/**
|
|
|
|
|
* @brief Controller handle button down.
|
|
|
|
|
* @param appstate Application state supplied by SDL.
|
|
|
|
|
* @param event SDL input event to process.
|
|
|
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
|
|
|
|
* @throws AKERR_* Propagates an error reported by a delegated operation.
|
|
|
|
|
*/
|
2026-05-06 23:18:42 -04:00
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_handle_button_down(void *appstate, SDL_Event *event);
|
2026-07-30 01:10:31 -04:00
|
|
|
/**
|
|
|
|
|
* @brief Controller handle button up.
|
|
|
|
|
* @param appstate Application state supplied by SDL.
|
|
|
|
|
* @param event SDL input event to process.
|
|
|
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
|
|
|
|
* @throws AKERR_* Propagates an error reported by a delegated operation.
|
|
|
|
|
*/
|
2026-05-06 23:18:42 -04:00
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_handle_button_up(void *appstate, SDL_Event *event);
|
2026-07-30 01:10:31 -04:00
|
|
|
/**
|
|
|
|
|
* @brief Controller handle added.
|
|
|
|
|
* @param appstate Application state supplied by SDL.
|
|
|
|
|
* @param event SDL input event to process.
|
|
|
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
|
|
|
|
* @throws AKERR_* Propagates an error reported by a delegated operation.
|
|
|
|
|
*/
|
2026-05-06 23:18:42 -04:00
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_handle_added(void *appstate, SDL_Event *event);
|
2026-07-30 01:10:31 -04:00
|
|
|
/**
|
|
|
|
|
* @brief Controller handle removed.
|
|
|
|
|
* @param appstate Application state supplied by SDL.
|
|
|
|
|
* @param event SDL input event to process.
|
|
|
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
|
|
|
|
* @throws AKERR_* Propagates an error reported by a delegated operation.
|
|
|
|
|
*/
|
2026-05-06 23:18:42 -04:00
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_handle_removed(void *appstate, SDL_Event *event);
|
2025-08-03 21:42:12 -04:00
|
|
|
|
2026-07-30 01:10:31 -04:00
|
|
|
/**
|
|
|
|
|
* @brief Controller pushmap.
|
|
|
|
|
* @param controlmapid Index of the control map to update.
|
|
|
|
|
* @param control Control binding to append to the selected map.
|
|
|
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
|
|
|
|
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
|
|
|
|
* @throws AKERR_OUTOFBOUNDS When the corresponding validation or operation fails.
|
|
|
|
|
*/
|
2026-05-06 23:18:42 -04:00
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_pushmap(int controlmapid, akgl_Control *control);
|
2026-05-06 12:02:36 -04:00
|
|
|
|
2026-07-30 01:10:31 -04:00
|
|
|
/**
|
|
|
|
|
* @brief Controller default.
|
|
|
|
|
* @param controlmapid Index of the control map to update.
|
|
|
|
|
* @param actorname Registry name of the controlled actor.
|
|
|
|
|
* @param kbid SDL keyboard identifier assigned to the map.
|
|
|
|
|
* @param jsid SDL joystick or gamepad identifier assigned to the map.
|
|
|
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
|
|
|
|
* @throws AKERR_OUTOFBOUNDS When the corresponding validation or operation fails.
|
|
|
|
|
* @throws AKGL_ERR_REGISTRY When the corresponding validation or operation fails.
|
|
|
|
|
*/
|
2026-05-06 23:18:42 -04:00
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_default(int controlmapid, char *actorname, int kbid, int jsid);
|
2026-05-21 21:43:51 -04:00
|
|
|
|
2026-07-30 01:10:31 -04:00
|
|
|
/**
|
|
|
|
|
* @brief Controller open gamepads.
|
|
|
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
|
|
|
|
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
|
|
|
|
*/
|
2026-05-21 21:43:51 -04:00
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_open_gamepads(void);
|
2026-07-31 06:57:50 -04:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Take the oldest waiting keystroke, if there is one.
|
|
|
|
|
*
|
|
|
|
|
* The rest of this header is built around the host pumping SDL events into
|
|
|
|
|
* akgl_controller_handle_event(), which suits a game loop and does not suit an
|
|
|
|
|
* embedded interpreter asking "is there a key waiting, yes or no" without
|
|
|
|
|
* owning the event loop itself. Every key press that reaches
|
|
|
|
|
* akgl_controller_handle_event() is recorded in a fixed ring buffer first,
|
|
|
|
|
* whether or not a control map claims it, and this drains that buffer one
|
|
|
|
|
* keystroke per call.
|
|
|
|
|
*
|
|
|
|
|
* When no key is waiting the call still succeeds: @p available is set to
|
|
|
|
|
* `false` and @p keycode to 0. The caller polls, it does not block.
|
|
|
|
|
*
|
|
|
|
|
* A full buffer drops the *newest* keystroke rather than the oldest, so what
|
|
|
|
|
* was typed first is what is read first. This runs on whichever thread pumps
|
|
|
|
|
* events; it is not synchronized.
|
|
|
|
|
*
|
|
|
|
|
* @param keycode Output destination populated with the SDL keycode, or 0.
|
|
|
|
|
* @param available Output destination set to `true` when a keystroke was taken.
|
|
|
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
|
|
|
|
* @throws AKERR_NULLPOINTER When the corresponding validation or operation fails.
|
|
|
|
|
*/
|
|
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_poll_key(int *keycode, bool *available);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Discard every keystroke waiting in the buffer.
|
|
|
|
|
*
|
|
|
|
|
* For a caller that has been ignoring input and does not want a backlog acted
|
|
|
|
|
* on the moment it starts polling again.
|
|
|
|
|
*
|
|
|
|
|
* @return `NULL` on success, otherwise an error context owned by the caller.
|
|
|
|
|
*/
|
|
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_flush_keys(void);
|
2025-08-03 21:42:12 -04:00
|
|
|
#endif // _CONTROLLER_H_
|