Files
libakgl/src/controller.c

349 lines
13 KiB
C

#include <SDL3/SDL.h>
#include <akerror.h>
#include <akgl/heap.h>
#include <akgl/registry.h>
#include <akgl/game.h>
#include <akgl/controller.h>
akgl_ControlMap GAME_ControlMaps[AKGL_MAX_CONTROL_MAPS];
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_list_keyboards(void)
{
int count;
SDL_KeyboardID *keyboards = SDL_GetKeyboards(&count);
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, keyboards, AKERR_NULLPOINTER, "%s", SDL_GetError());
for (int i = 0; i < count; i++) {
const char *name = SDL_GetKeyboardNameForID(keyboards[i]);
SDL_Log("Keyboard %d: ID %u, Name: %s\n", i, keyboards[i], name);
}
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_open_gamepads(void)
{
int count = 0;
int i = 0;
SDL_JoystickID *gamepads = NULL;
SDL_Gamepad *gamepad = NULL;
PREPARE_ERROR(e);
if ( SDL_HasGamepad() ) {
gamepads = SDL_GetGamepads(&count);
if ( count > 0 ) {
FAIL_ZERO_RETURN(e, gamepads, AKERR_NULLPOINTER, "%s", SDL_GetError());
for ( i = 0; i < count ; i++ ) {
gamepad = SDL_OpenGamepad(gamepads[i]);
FAIL_ZERO_RETURN(e, gamepad, AKERR_NULLPOINTER, "%s", SDL_GetError());
SDL_Log("Gamepad %d is %s", i, SDL_GetGamepadNameForID(gamepads[i]));
}
SDL_free(gamepads);
} else {
SDL_Log("No gamepads enumerated");
}
} else {
SDL_Log("No gamepads connected");
}
SUCCEED_RETURN(e);
}
akerr_ErrorContext *akgl_controller_handle_event(void *appstate, SDL_Event *event)
{
int i = 0;
int j = 0;
int eventButtonComboMatch = 0;
akgl_ControlMap *curmap = NULL;
akgl_Control *curcontrol = NULL;
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL appstate");
FAIL_ZERO_RETURN(errctx, event, AKERR_NULLPOINTER, "NULL event");
ATTEMPT {
for ( i = 0 ; i < AKGL_MAX_CONTROL_MAPS; i++ ) {
curmap = &GAME_ControlMaps[i];
if ( curmap->target == NULL ) {
continue;
}
//SDL_Log("Control map %d maps to actor %s", i, curmap->target->name);
//SDL_Log("event from keyboard %d", event->key.which);
for ( j = 0; j < AKGL_MAX_CONTROLS; j++ ) {
curcontrol = &curmap->controls[j];
//SDL_Log("button/key is processed by controlmap %d control %d", i, j);
//SDL_Log("event %d -> control on %d off %d", event->type, curcontrol->event_on, curcontrol->event_off);
// This controlmap processes this control
eventButtonComboMatch = (
((event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN ||
event->type == SDL_EVENT_GAMEPAD_BUTTON_UP) &&
event->gbutton.which == curmap->jsid &&
event->gbutton.button == curcontrol->button) ||
((event->type == SDL_EVENT_KEY_DOWN ||
event->type == SDL_EVENT_KEY_UP) &&
event->key.which == curmap->kbid &&
event->key.key == curcontrol->key)
);
if ( event->type == curcontrol->event_on && eventButtonComboMatch) {
CATCH(errctx, curcontrol->handler_on(curmap->target, event));
goto _akgl_controller_handle_event_success;
} else if ( event->type == curcontrol->event_off && eventButtonComboMatch ) {
CATCH(errctx, curcontrol->handler_off(curmap->target, event));
goto _akgl_controller_handle_event_success;
}
}
}
_akgl_controller_handle_event_success:
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true);
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *gamepad_handle_button_down(void *appstate, SDL_Event *event)
{
akgl_Actor *player = NULL;
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL appstate");
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL event");
player = SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, "player", NULL);
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "Player actor does not exist");
if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_DOWN ||
event->key.key == SDLK_DOWN ) {
SDL_Log("Processing dpad down : state %d", player->state);
AKGL_BITMASK_ADD(player->state, AKGL_ACTOR_STATE_MOVING_DOWN);
if ( !player->movement_controls_face ) {
AKGL_BITMASK_DEL(player->state, AKGL_ACTOR_STATE_FACE_ALL);
AKGL_BITMASK_ADD(player->state, AKGL_ACTOR_STATE_FACE_DOWN);
}
SDL_Log("New state : %d", player->state);
} else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_UP ||
event->key.key == SDLK_UP ) {
SDL_Log("Processing dpad up");
AKGL_BITMASK_ADD(player->state, AKGL_ACTOR_STATE_MOVING_UP);
if ( !player->movement_controls_face ) {
AKGL_BITMASK_DEL(player->state, AKGL_ACTOR_STATE_FACE_ALL);
AKGL_BITMASK_ADD(player->state, AKGL_ACTOR_STATE_FACE_UP);
}
SDL_Log("New state : %d", player->state);
} else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_LEFT ||
event->key.key == SDLK_LEFT ) {
SDL_Log("Processing dpad left");
AKGL_BITMASK_ADD(player->state, AKGL_ACTOR_STATE_MOVING_LEFT);
if ( !player->movement_controls_face ) {
AKGL_BITMASK_DEL(player->state, AKGL_ACTOR_STATE_FACE_ALL);
AKGL_BITMASK_ADD(player->state, AKGL_ACTOR_STATE_FACE_LEFT);
}
SDL_Log("New state : %d", player->state);
} else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_RIGHT ||
event->key.key == SDLK_RIGHT ) {
SDL_Log("Processing dpad right");
AKGL_BITMASK_ADD(player->state, AKGL_ACTOR_STATE_MOVING_RIGHT);
if ( !player->movement_controls_face ) {
AKGL_BITMASK_DEL(player->state, AKGL_ACTOR_STATE_FACE_ALL);
AKGL_BITMASK_ADD(player->state, AKGL_ACTOR_STATE_FACE_RIGHT);
}
SDL_Log("New state : %d", player->state);
}
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *gamepad_handle_button_up(void *appstate, SDL_Event *event)
{
akgl_Actor *player = NULL;
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL appstate");
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL event");
player = SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, "player", NULL);
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "Player actor does not exist");
if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_DOWN ||
event->key.key == SDLK_DOWN ) {
SDL_Log("processing down release");
AKGL_BITMASK_DEL(player->state, AKGL_ACTOR_STATE_MOVING_DOWN);
player->curSpriteFrameId = 0;
SDL_Log("New state : %d", player->state);
} else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_UP ||
event->key.key == SDLK_UP ) {
SDL_Log("processing up release");
AKGL_BITMASK_DEL(player->state, AKGL_ACTOR_STATE_MOVING_UP);
player->curSpriteFrameId = 0;
SDL_Log("New state : %d", player->state);
} else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_RIGHT ||
event->key.key == SDLK_RIGHT) {
SDL_Log("processing right release");
AKGL_BITMASK_DEL(player->state, AKGL_ACTOR_STATE_MOVING_RIGHT);
player->curSpriteFrameId = 0;
SDL_Log("New state : %d", player->state);
} else if ( event->gbutton.button == SDL_GAMEPAD_BUTTON_DPAD_LEFT ||
event->key.key == SDLK_LEFT ) {
SDL_Log("processing left release");
AKGL_BITMASK_DEL(player->state, AKGL_ACTOR_STATE_MOVING_LEFT);
player->curSpriteFrameId = 0;
SDL_Log("New state : %d", player->state);
}
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *gamepad_handle_added(void *appstate, SDL_Event *event)
{
SDL_JoystickID which;
SDL_Gamepad *gamepad = NULL;
char *mapping = NULL;
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL appstate");
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL event");
which = event->gbutton.which;
gamepad = SDL_GetGamepadFromID(which);
if (!gamepad) {
SDL_Log("Gamepad #%u add, but not opened: %s", (unsigned int) which, SDL_GetError());
gamepad = SDL_OpenGamepad(which);
SDL_Log("Gamepad #%u opened: %s", (unsigned int) which, SDL_GetError());
mapping = SDL_GetGamepadMapping(gamepad);
if ( mapping == NULL ) {
SDL_Log("Gamepad #%u has no mapping!", (unsigned int) which);
} else if ( mapping != NULL ) {
SDL_Log("Gamepad #%u mapping : %s", (unsigned int) which, mapping);
SDL_free(mapping);
}
} else {
SDL_Log("Gamepad #%u ('%s') added", (unsigned int) which, SDL_GetGamepadName(gamepad));
}
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *gamepad_handle_removed(void *appstate, SDL_Event *event)
{
SDL_JoystickID which;
SDL_Gamepad *gamepad = NULL;
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL appstate");
FAIL_ZERO_RETURN(errctx, appstate, AKERR_NULLPOINTER, "NULL event");
which = event->gbutton.which;
gamepad = SDL_GetGamepadFromID(which);
if (gamepad) {
SDL_CloseGamepad(gamepad); /* the joystick was unplugged. */
}
SDL_Log("Gamepad #%u removed", (unsigned int) which);
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_pushmap(int controlmapid, akgl_Control *control)
{
int newmapid = 0;
PREPARE_ERROR(errctx);
ATTEMPT {
FAIL_ZERO_RETURN(errctx, control, AKERR_NULLPOINTER, "NULL Control");
FAIL_NONZERO_RETURN(errctx, (controlmapid >= AKGL_MAX_CONTROL_MAPS), AKERR_OUTOFBOUNDS, "ID %d exceeds maximum %d", controlmapid, AKGL_MAX_CONTROL_MAPS);
newmapid = GAME_ControlMaps[controlmapid].nextMap;
FAIL_ZERO_RETURN(errctx, (AKGL_MAX_CONTROLS - newmapid), AKERR_OUTOFBOUNDS, "Control map ID %d is full", controlmapid);
memcpy((void *)&GAME_ControlMaps[controlmapid].controls[newmapid], control, sizeof(akgl_Control));
GAME_ControlMaps[controlmapid].nextMap = newmapid + 1;
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true);
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_default(int controlmapid, char *actorname, int kbid, int jsid)
{
akgl_ControlMap *controlmap;
akgl_Control control;
PREPARE_ERROR(errctx);
ATTEMPT {
// set up the control map
FAIL_NONZERO_RETURN(errctx, (controlmapid >= AKGL_MAX_CONTROL_MAPS), AKERR_OUTOFBOUNDS, "ID %d exceeds maximum %d", controlmapid, AKGL_MAX_CONTROL_MAPS);
memset((void *)&control, 0x00, sizeof(akgl_Control));
controlmap = &GAME_ControlMaps[controlmapid];
controlmap->kbid = kbid;
controlmap->jsid = jsid;
controlmap->target = SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, actorname, NULL);
FAIL_ZERO_BREAK(errctx, controlmap->target, AKERR_REGISTRY, "Actor %s not found in registry", actorname);
// ---- KEYBOARD CONTROLS ----
// Move down
control.key = SDLK_DOWN;
control.event_on = SDL_EVENT_KEY_DOWN;
control.event_off = SDL_EVENT_KEY_UP;
control.handler_on = &akgl_Actor_cmhf_down_on;
control.handler_off = &akgl_Actor_cmhf_down_off;
CATCH(errctx, akgl_controller_pushmap(controlmapid, &control));
// Move up
control.key = SDLK_UP;
control.event_on = SDL_EVENT_KEY_DOWN;
control.event_off = SDL_EVENT_KEY_UP;
control.handler_on = &akgl_Actor_cmhf_up_on;
control.handler_off = &akgl_Actor_cmhf_up_off;
CATCH(errctx, akgl_controller_pushmap(controlmapid, &control));
// Move left
control.key = SDLK_LEFT;
control.event_on = SDL_EVENT_KEY_DOWN;
control.event_off = SDL_EVENT_KEY_UP;
control.handler_on = &akgl_Actor_cmhf_left_on;
control.handler_off = &akgl_Actor_cmhf_left_off;
CATCH(errctx, akgl_controller_pushmap(controlmapid, &control));
// Move right
control.key = SDLK_RIGHT;
control.event_on = SDL_EVENT_KEY_DOWN;
control.event_off = SDL_EVENT_KEY_UP;
control.handler_on = &akgl_Actor_cmhf_right_on;
control.handler_off = &akgl_Actor_cmhf_right_off;
CATCH(errctx, akgl_controller_pushmap(controlmapid, &control));
control.key = 0;
// ----- GAMEPAD CONTROLS
// Move down
control.button = SDL_GAMEPAD_BUTTON_DPAD_DOWN;
control.event_on = SDL_EVENT_GAMEPAD_BUTTON_DOWN;
control.event_off = SDL_EVENT_GAMEPAD_BUTTON_UP;
control.handler_on = &akgl_Actor_cmhf_down_on;
control.handler_off = &akgl_Actor_cmhf_down_off;
CATCH(errctx, akgl_controller_pushmap(controlmapid, &control));
// Move up
control.button = SDL_GAMEPAD_BUTTON_DPAD_UP;
control.event_on = SDL_EVENT_GAMEPAD_BUTTON_DOWN;
control.event_off = SDL_EVENT_GAMEPAD_BUTTON_UP;
control.handler_on = &akgl_Actor_cmhf_up_on;
control.handler_off = &akgl_Actor_cmhf_up_off;
CATCH(errctx, akgl_controller_pushmap(controlmapid, &control));
// Move left
control.button = SDL_GAMEPAD_BUTTON_DPAD_LEFT;
control.event_on = SDL_EVENT_GAMEPAD_BUTTON_DOWN;
control.event_off = SDL_EVENT_GAMEPAD_BUTTON_UP;
control.handler_on = &akgl_Actor_cmhf_left_on;
control.handler_off = &akgl_Actor_cmhf_left_off;
CATCH(errctx, akgl_controller_pushmap(controlmapid, &control));
// Move right
control.button = SDL_GAMEPAD_BUTTON_DPAD_RIGHT;
control.event_on = SDL_EVENT_GAMEPAD_BUTTON_DOWN;
control.event_off = SDL_EVENT_GAMEPAD_BUTTON_UP;
control.handler_on = &akgl_Actor_cmhf_right_on;
control.handler_off = &akgl_Actor_cmhf_right_off;
CATCH(errctx, akgl_controller_pushmap(controlmapid, &control));
SUCCEED_RETURN(errctx);
} CLEANUP {
} PROCESS(errctx) {
} FINISH(errctx, true);
}