Use game controller APIs instead of the joystick ones to be able to use the gamepad button DB file.master
@@ -69,7 +69,7 @@ include_directories( | |||
"${CMAKE_HOME_DIRECTORY}/dependencies/SDL2_image/include" | |||
"${CMAKE_HOME_DIRECTORY}/dependencies/SDL2_ttf/include" | |||
"${CMAKE_HOME_DIRECTORY}/subprojects/ini-config/subprojects/minIni/dev" | |||
"${CMAKE_HOME_DIRECTORY}/subprojects/ini-config" | |||
"${CMAKE_HOME_DIRECTORY}/subprojects/ini-config/source" | |||
"${CMAKE_HOME_DIRECTORY}/subprojects/bdd-for-c" | |||
"${CMAKE_HOME_DIRECTORY}/subprojects/bdd-for-c-mocks" | |||
"${CMAKE_HOME_DIRECTORY}/subprojects/midi-utils" | |||
@@ -142,12 +142,12 @@ add_executable( | |||
${IZ_EXECUTABLE_TYPE} | |||
subprojects/ini-config/subprojects/minIni/dev/minIni.h | |||
subprojects/ini-config/subprojects/minIni/dev/minIni.c | |||
subprojects/ini-config/ini-config.h | |||
subprojects/ini-config/ini-config.c | |||
subprojects/ini-config/types/int.h | |||
subprojects/ini-config/types/int.c | |||
subprojects/ini-config/types/string.h | |||
subprojects/ini-config/types/string.c | |||
subprojects/ini-config/source/ini-config.h | |||
subprojects/ini-config/source/ini-config.c | |||
subprojects/ini-config/source/types/int.h | |||
subprojects/ini-config/source/types/int.c | |||
subprojects/ini-config/source/types/string.h | |||
subprojects/ini-config/source/types/string.c | |||
subprojects/midi-utils/midi-utils.h | |||
subprojects/midi-utils/midi-utils.c | |||
${IZ_GETOPT_DEPENDENCIES} | |||
@@ -158,8 +158,8 @@ add_executable( | |||
src/packages/game/IZ_app.h | |||
src/packages/game/IZ_app.c | |||
src/packages/game/main.c | |||
src/packages/game/input/IZ_joystick.c | |||
src/packages/game/input/IZ_joystick.h | |||
src/packages/game/input/IZ_gamecontroller.c | |||
src/packages/game/input/IZ_gamecontroller.h | |||
src/packages/game/input/IZ_keyboard.c | |||
src/packages/game/input/IZ_keyboard.h | |||
src/packages/game/geometry/IZ_vector2d.c | |||
@@ -242,23 +242,26 @@ add_executable( | |||
subprojects/bdd-for-c/bdd-for-c.h | |||
subprojects/bdd-for-c-mocks/bdd-for-c-mocks.h | |||
subprojects/ini-config/ini-config.h | |||
__mocks__/subprojects/ini-config/ini-config.mock.h | |||
subprojects/ini-config/source/ini-config.h | |||
__mocks__/subprojects/ini-config/source/ini-config.mock.h | |||
__mocks__/subprojects/SDL/SDL_keyboard.mock.h | |||
__mocks__/subprojects/SDL/SDL_events.mock.h | |||
__mocks__/subprojects/SDL/SDL_joystick.mock.h | |||
__mocks__/subprojects/SDL/SDL_gamecontroller.mock.h | |||
__mocks__/subprojects/SDL/SDL_stdinc.mock.h | |||
__mocks__/subprojects/portmidi/portmidi.mock.h | |||
src/packages/log/IZ_log.h | |||
__mocks__/src/packages/log/IZ_log.mock.h | |||
src/packages/config/IZ_config_guid.h | |||
src/packages/config/IZ_config_guid.c | |||
src/packages/game/input/IZ_keyboard.h | |||
src/packages/game/input/IZ_keyboard.c | |||
src/packages/game/input/IZ_joystick.h | |||
src/packages/game/input/IZ_joystick.c | |||
src/packages/game/input/IZ_gamecontroller.h | |||
src/packages/game/input/IZ_gamecontroller.c | |||
src/packages/game/input/IZ_midi.h | |||
src/packages/game/input/IZ_midi.c | |||
@@ -350,12 +353,12 @@ add_executable( | |||
dependencies/sqlite/sqlite3.c | |||
subprojects/ini-config/subprojects/minIni/dev/minIni.h | |||
subprojects/ini-config/subprojects/minIni/dev/minIni.c | |||
subprojects/ini-config/ini-config.h | |||
subprojects/ini-config/ini-config.c | |||
subprojects/ini-config/types/int.h | |||
subprojects/ini-config/types/int.c | |||
subprojects/ini-config/types/string.h | |||
subprojects/ini-config/types/string.c | |||
subprojects/ini-config/source/ini-config.h | |||
subprojects/ini-config/source/ini-config.c | |||
subprojects/ini-config/source/types/int.h | |||
subprojects/ini-config/source/types/int.c | |||
subprojects/ini-config/source/types/string.h | |||
subprojects/ini-config/source/types/string.c | |||
src/packages/log/IZ_intercept.h | |||
src/packages/log/IZ_intercept.c | |||
src/packages/server/main.c | |||
@@ -394,12 +397,12 @@ add_executable( | |||
asset-inv | |||
subprojects/ini-config/subprojects/minIni/dev/minIni.h | |||
subprojects/ini-config/subprojects/minIni/dev/minIni.c | |||
subprojects/ini-config/ini-config.h | |||
subprojects/ini-config/ini-config.c | |||
subprojects/ini-config/types/int.h | |||
subprojects/ini-config/types/int.c | |||
subprojects/ini-config/types/string.h | |||
subprojects/ini-config/types/string.c | |||
subprojects/ini-config/source/ini-config.h | |||
subprojects/ini-config/source/ini-config.c | |||
subprojects/ini-config/source/types/int.h | |||
subprojects/ini-config/source/types/int.c | |||
subprojects/ini-config/source/types/string.h | |||
subprojects/ini-config/source/types/string.c | |||
src/packages/common/IZ_common.h | |||
src/packages/asset-inv/main.c | |||
) | |||
@@ -9,6 +9,10 @@ mock_modes(IZ_LogInfo) { | |||
IZ_LOG_INFO_LOG, | |||
}; | |||
mock(IZ_LOG_DATE_FUNCTION) char* IZ_LOG_DATE_FUNCTION(void) { | |||
mock_return(IZ_LOG_DATE_FUNCTION) ""; | |||
} | |||
mock(IZ_LogInfo) void IZ_LogInfo(IZ_LogCategory category, const char* context, const char* fmt, ...) { | |||
mock_mode_if(IZ_LogInfo, IZ_LOG_INFO_SUPPRESS) { | |||
mock_return(IZ_LogInfo); | |||
@@ -0,0 +1,48 @@ | |||
#ifndef SDL_GAMECONTROLLER_MOCK_H | |||
#define SDL_GAMECONTROLLER_MOCK_H | |||
#include <bdd-for-c-mocks.h> | |||
#include <SDL_gamecontroller.h> | |||
#include <SDL_rwops.h> | |||
#include "../../../src/packages/common/IZ_common.h" | |||
#define MOCK_OPEN_JOYSTICKS 1 | |||
mock(SDL_GameControllerOpen) SDL_GameController* SDL_GameControllerOpen(i32 device_index) { | |||
static void* joystick = (void*) 1; | |||
mock_return(SDL_GameControllerOpen) (SDL_GameController*) &joystick; | |||
} | |||
mock(SDL_NumJoysticks) i32 SDL_NumJoysticks(void) { | |||
mock_return(SDL_NumJoysticks) MOCK_OPEN_JOYSTICKS; | |||
} | |||
mock(SDL_JoystickInstanceID) i32 SDL_JoystickInstanceID(SDL_Joystick* joystick) { | |||
mock_return(SDL_JoystickInstanceID) 0; | |||
} | |||
mock(SDL_GameControllerClose) void SDL_GameControllerClose(SDL_GameController* _joystick) { | |||
mock_return(SDL_GameControllerClose); | |||
} | |||
mock(SDL_JoystickGetGUID) SDL_JoystickGUID SDL_JoystickGetGUID(SDL_Joystick* joystick) { | |||
mock_return(SDL_JoystickGetGUID) (SDL_JoystickGUID) { | |||
.data = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, } | |||
}; | |||
} | |||
mock(SDL_GameControllerAddMappingsFromRW) int SDL_GameControllerAddMappingsFromRW(SDL_RWops *rw, int freerw) { | |||
mock_return(SDL_GameControllerAddMappingsFromRW) 1; | |||
} | |||
mock(SDL_GameControllerFromInstanceID) SDL_GameController* SDL_GameControllerFromInstanceID(SDL_JoystickID instance_id) { | |||
static void* joystick = (void*) 1; | |||
mock_return(SDL_GameControllerFromInstanceID) (SDL_GameController*) &joystick; | |||
} | |||
mock(SDL_GameControllerGetJoystick) SDL_Joystick* SDL_GameControllerGetJoystick(SDL_GameController* gamecontroller) { | |||
static void* joystick = (void*) 1; | |||
mock_return(SDL_GameControllerGetJoystick) (SDL_Joystick*) &joystick; | |||
} | |||
#endif |
@@ -1,43 +0,0 @@ | |||
#ifndef SDL_JOYSTICK_MOCK_H | |||
#define SDL_JOYSTICK_MOCK_H | |||
#include <bdd-for-c-mocks.h> | |||
#include <SDL_joystick.h> | |||
#include <SDL_rwops.h> | |||
#include "../../../src/packages/common/IZ_common.h" | |||
#define MOCK_OPEN_JOYSTICKS 1 | |||
mock(SDL_JoystickOpen) SDL_Joystick* SDL_JoystickOpen(i32 device_index) { | |||
static void* joystick = (void*) 1; | |||
mock_return(SDL_JoystickOpen) (SDL_Joystick*) &joystick; | |||
} | |||
mock(SDL_NumJoysticks) i32 SDL_NumJoysticks(void) { | |||
mock_return(SDL_NumJoysticks) MOCK_OPEN_JOYSTICKS; | |||
} | |||
mock(SDL_JoystickInstanceID) i32 SDL_JoystickInstanceID(SDL_Joystick* joystick) { | |||
mock_return(SDL_JoystickInstanceID) 0; | |||
} | |||
mock(SDL_JoystickClose) void SDL_JoystickClose(SDL_Joystick* _joystick) { | |||
mock_return(SDL_JoystickClose); | |||
} | |||
mock(SDL_JoystickGetGUID) SDL_JoystickGUID SDL_JoystickGetGUID(SDL_Joystick* joystick) { | |||
mock_return(SDL_JoystickGetGUID) (SDL_JoystickGUID) { | |||
.data = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, } | |||
}; | |||
} | |||
mock(SDL_GameControllerAddMappingsFromRW) int SDL_GameControllerAddMappingsFromRW(SDL_RWops *rw, int freerw) { | |||
mock_return(SDL_GameControllerAddMappingsFromRW) 0; | |||
} | |||
mock(SDL_JoystickFromInstanceID) SDL_Joystick* SDL_JoystickFromInstanceID(SDL_JoystickID instance_id) { | |||
static void* joystick = (void*) 1; | |||
mock_return(SDL_JoystickFromInstanceID) (SDL_Joystick*) &joystick; | |||
} | |||
#endif |
@@ -16,59 +16,59 @@ mock(INI_ConfigGetCommandlineOption) const char* INI_ConfigGetCommandlineOption( | |||
mock_return(INI_ConfigGetCommandlineOption) ""; | |||
} | |||
mock(INI_ConfigLoadU8) void INI_ConfigLoadU8(INI_ConfigItem* _item, const char* _config_path) { | |||
mock(INI_ConfigLoadU8) void INI_ConfigLoadU8(INI_ConfigItem* _item, const char* _config_path, void* _item_array) { | |||
mock_return(INI_ConfigLoadU8); | |||
} | |||
mock(INI_ConfigLoadU16) void INI_ConfigLoadU16(INI_ConfigItem* _item, const char* _config_path) { | |||
mock(INI_ConfigLoadU16) void INI_ConfigLoadU16(INI_ConfigItem* _item, const char* _config_path, void* _item_array) { | |||
mock_return(INI_ConfigLoadU16); | |||
} | |||
mock(INI_ConfigLoadU32) void INI_ConfigLoadU32(INI_ConfigItem* _item, const char* _config_path) { | |||
mock(INI_ConfigLoadU32) void INI_ConfigLoadU32(INI_ConfigItem* _item, const char* _config_path, void* _item_array) { | |||
mock_return(INI_ConfigLoadU32); | |||
} | |||
mock(INI_ConfigLoadI8) void INI_ConfigLoadI8(INI_ConfigItem* _item, const char* _config_path) { | |||
mock(INI_ConfigLoadI8) void INI_ConfigLoadI8(INI_ConfigItem* _item, const char* _config_path, void* _item_array) { | |||
mock_return(INI_ConfigLoadI8); | |||
} | |||
mock(INI_ConfigLoadI16) void INI_ConfigLoadI16(INI_ConfigItem* _item, const char* _config_path) { | |||
mock(INI_ConfigLoadI16) void INI_ConfigLoadI16(INI_ConfigItem* _item, const char* _config_path, void* _item_array) { | |||
mock_return(INI_ConfigLoadI16); | |||
} | |||
mock(INI_ConfigLoadI32) void INI_ConfigLoadI32(INI_ConfigItem* _item, const char* _config_path) { | |||
mock(INI_ConfigLoadI32) void INI_ConfigLoadI32(INI_ConfigItem* _item, const char* _config_path, void* _item_array) { | |||
mock_return(INI_ConfigLoadI32); | |||
} | |||
mock(INI_ConfigLoadString) void INI_ConfigLoadString(INI_ConfigItem* _item, const char* _config_path) { | |||
mock(INI_ConfigLoadString) void INI_ConfigLoadString(INI_ConfigItem* _item, const char* _config_path, void* _item_array) { | |||
mock_return(INI_ConfigLoadString); | |||
} | |||
mock(INI_ConfigSaveU8) INI_ConfigSaveItemResult INI_ConfigSaveU8(INI_ConfigItem* _item, const char* _config_path) { | |||
mock(INI_ConfigSaveU8) INI_ConfigSaveItemResult INI_ConfigSaveU8(INI_ConfigItem* _item, const char* _config_path, void* _item_array) { | |||
mock_return(INI_ConfigSaveU8) INI_CONFIG_SAVE_ITEM_OK; | |||
} | |||
mock(INI_ConfigSaveU16) INI_ConfigSaveItemResult INI_ConfigSaveU16(INI_ConfigItem* _item, const char* _config_path) { | |||
mock(INI_ConfigSaveU16) INI_ConfigSaveItemResult INI_ConfigSaveU16(INI_ConfigItem* _item, const char* _config_path, void* _item_array) { | |||
mock_return(INI_ConfigSaveU16) INI_CONFIG_SAVE_ITEM_OK; | |||
} | |||
mock(INI_ConfigSaveU32) INI_ConfigSaveItemResult INI_ConfigSaveU32(INI_ConfigItem* _item, const char* _config_path) { | |||
mock(INI_ConfigSaveU32) INI_ConfigSaveItemResult INI_ConfigSaveU32(INI_ConfigItem* _item, const char* _config_path, void* _item_array) { | |||
mock_return(INI_ConfigSaveU32) INI_CONFIG_SAVE_ITEM_OK; | |||
} | |||
mock(INI_ConfigSaveI8) INI_ConfigSaveItemResult INI_ConfigSaveI8(INI_ConfigItem* _item, const char* _config_path) { | |||
mock(INI_ConfigSaveI8) INI_ConfigSaveItemResult INI_ConfigSaveI8(INI_ConfigItem* _item, const char* _config_path, void* _item_array) { | |||
mock_return(INI_ConfigSaveI8) INI_CONFIG_SAVE_ITEM_OK; | |||
} | |||
mock(INI_ConfigSaveI16) INI_ConfigSaveItemResult INI_ConfigSaveI16(INI_ConfigItem* _item, const char* _config_path) { | |||
mock(INI_ConfigSaveI16) INI_ConfigSaveItemResult INI_ConfigSaveI16(INI_ConfigItem* _item, const char* _config_path, void* _item_array) { | |||
mock_return(INI_ConfigSaveI16) INI_CONFIG_SAVE_ITEM_OK; | |||
} | |||
mock(INI_ConfigSaveI32) INI_ConfigSaveItemResult INI_ConfigSaveI32(INI_ConfigItem* _item, const char* _config_path) { | |||
mock(INI_ConfigSaveI32) INI_ConfigSaveItemResult INI_ConfigSaveI32(INI_ConfigItem* _item, const char* _config_path, void* _item_array) { | |||
mock_return(INI_ConfigSaveI32) INI_CONFIG_SAVE_ITEM_OK; | |||
} | |||
mock(INI_ConfigSaveString) INI_ConfigSaveItemResult INI_ConfigSaveString(INI_ConfigItem* _item, const char* _config_path) { | |||
mock(INI_ConfigSaveString) INI_ConfigSaveItemResult INI_ConfigSaveString(INI_ConfigItem* _item, const char* _config_path, void* _item_array) { | |||
mock_return(INI_ConfigSaveString) INI_CONFIG_SAVE_ITEM_OK; | |||
} | |||
@@ -3,29 +3,22 @@ | |||
#endif | |||
#include <bdd-for-c.h> | |||
#include <subprojects/SDL/SDL_keyboard.mock.h> | |||
#include <subprojects/SDL/SDL_joystick.mock.h> | |||
#include <subprojects/SDL/SDL_gamecontroller.mock.h> | |||
#include <subprojects/SDL/SDL_stdinc.mock.h> | |||
#include <subprojects/minIni/minIni.mock.h> | |||
#include <subprojects/portmidi/portmidi.mock.h> | |||
#include <stdinc/IZ_string.mock.h> | |||
#include <stdinc/IZ_stdlib.mock.h> | |||
#include <subprojects/ini-config/ini-config.mock.h> | |||
#include <log/IZ_log.mock.h> | |||
#include <subprojects/ini-config/source/ini-config.mock.h> | |||
#include <game/input/IZ_keyboard.h> | |||
#include <game/input/IZ_joystick.h> | |||
#include <game/input/IZ_gamecontroller.h> | |||
#include <game/input/IZ_midi.h> | |||
i16 GenerateAxisValueWithinThreshold(u16 threshold) { | |||
return rand() % threshold; | |||
} | |||
i16 GenerateAxisValueOutsideThreshold(u16 threshold) { | |||
return threshold + (rand() % (RAND_MAX - threshold - 1)) + 1; | |||
} | |||
spec("input") { | |||
describe("joystick") { | |||
describe("gamecontroller") { | |||
describe("Initialize") { | |||
static IZ_JoystickState state[IZ_PLAYERS]; | |||
static IZ_GameControllerState state[IZ_PLAYERS]; | |||
after_each() { | |||
mock_reset(IZ_memcpy); | |||
@@ -36,7 +29,7 @@ spec("input") { | |||
} | |||
after_each() { | |||
mock_reset(SDL_JoystickOpen); | |||
mock_reset(SDL_GameControllerOpen); | |||
} | |||
after_each() { | |||
@@ -48,41 +41,41 @@ spec("input") { | |||
} | |||
it("sets initial state") { | |||
IZ_JoystickInitialize(&state, IZ_CONFIG_GAME_PATH, 0, NULL); | |||
IZ_GameControllerInitialize(&state, IZ_CONFIG_GAME_PATH, 0, NULL); | |||
check(mock_is_called(IZ_memcpy), "Initial state not loaded."); | |||
check(mock_is_called(SDL_NumJoysticks), "Connected joysticks not checked."); | |||
} | |||
it("calls load method") { | |||
IZ_JoystickInitialize(&state, IZ_CONFIG_GAME_PATH, 0, NULL); | |||
IZ_GameControllerInitialize(&state, IZ_CONFIG_GAME_PATH, 0, NULL); | |||
check(mock_is_called(INI_ConfigInitialize), "Config load function not called."); | |||
} | |||
it("calls save method") { | |||
IZ_JoystickInitialize(&state, IZ_CONFIG_GAME_PATH, 0, NULL); | |||
IZ_GameControllerInitialize(&state, IZ_CONFIG_GAME_PATH, 0, NULL); | |||
check(mock_is_called(INI_ConfigSave), "Config save function not called."); | |||
} | |||
it("opens device handles") { | |||
mock_set_expected_calls(SDL_JoystickOpen, MOCK_OPEN_JOYSTICKS); | |||
mock_set_expected_calls(SDL_GameControllerOpen, MOCK_OPEN_JOYSTICKS); | |||
IZ_JoystickInitialize(&state, IZ_CONFIG_GAME_PATH, 0, NULL); | |||
IZ_GameControllerInitialize(&state, IZ_CONFIG_GAME_PATH, 0, NULL); | |||
check( | |||
mock_get_expected_calls(SDL_JoystickOpen) == mock_get_actual_calls(SDL_JoystickOpen), | |||
"Call count mismatch for SDL_JoystickOpen() (expected %u, received %u).", | |||
mock_get_expected_calls(SDL_JoystickOpen), | |||
mock_get_actual_calls(SDL_JoystickOpen) | |||
mock_get_expected_calls(SDL_GameControllerOpen) == mock_get_actual_calls(SDL_GameControllerOpen), | |||
"Call count mismatch for SDL_GameControllerOpen() (expected %u, received %u).", | |||
mock_get_expected_calls(SDL_GameControllerOpen), | |||
mock_get_actual_calls(SDL_GameControllerOpen) | |||
); | |||
} | |||
} | |||
describe("HandleEvents") { | |||
static SDL_Event e; | |||
static IZ_JoystickState state[IZ_PLAYERS] = {}; | |||
static IZ_GameControllerState state[IZ_PLAYERS] = {}; | |||
static IZ_Action action[IZ_PLAYERS] = {}; | |||
u8 p; | |||
@@ -90,267 +83,75 @@ spec("input") { | |||
describe("on player %u", p) { | |||
describe("on axis motion events") { | |||
before_each() { | |||
e.type = SDL_JOYAXISMOTION; | |||
state[p].config.axis_threshold = 8000u; | |||
state[p].config.axis_threshold = IZ_GAME_CONTROLLER_AXIS_THRESHOLD; | |||
} | |||
describe("on primary horizontal direction") { | |||
before_each() { | |||
e.jaxis.axis = IZ_JOY_AXIS_DIRECTION_HORIZONTAL1; | |||
} | |||
it("handles positive motion") { | |||
e.jaxis.value = GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold); | |||
if (e.jaxis.value < 0) { | |||
e.jaxis.value = -e.jaxis.value; | |||
} | |||
action[p] = 0; | |||
printf("(axis value: %d) ", e.jaxis.value); | |||
IZ_JoystickHandleEvents(&state, &action, e); | |||
check( | |||
action[p] == (0x1 << IZ_ACTION_INDEX_RIGHT), | |||
"Action not set." | |||
); | |||
} | |||
for (u8 i = 0; i < SDL_CONTROLLER_AXIS_MAX; i += 1) { | |||
static const char* axis_name; | |||
axis_name = SDL_GameControllerGetStringForAxis(i); | |||
it("handles negative motion") { | |||
e.jaxis.value = -GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold); | |||
if (e.jaxis.value > 0) { | |||
e.jaxis.value = -e.jaxis.value; | |||
} | |||
it("handles %s positive motion activation", axis_name) { | |||
e.type = SDL_CONTROLLERAXISMOTION; | |||
e.caxis.axis = i; | |||
e.caxis.value = IZ_GAME_CONTROLLER_AXIS_THRESHOLD + 1; | |||
sprintf(state[p].config.control_mapping[0], "axis:+%s", axis_name); | |||
action[p] = 0; | |||
printf("(axis value: %d) ", e.jaxis.value); | |||
IZ_JoystickHandleEvents(&state, &action, e); | |||
check( | |||
action[p] == (0x1 << IZ_ACTION_INDEX_LEFT), | |||
"Action not set." | |||
); | |||
} | |||
it("handles neutral motion") { | |||
e.jaxis.value = GenerateAxisValueWithinThreshold(state[p].config.axis_threshold); | |||
action[p] = 0; | |||
printf("(axis value: %d) ", e.jaxis.value); | |||
IZ_JoystickHandleEvents(&state, &action, e); | |||
check( | |||
action[p] == 0, | |||
"Action not set." | |||
); | |||
} | |||
} | |||
describe("on secondary horizontal direction") { | |||
before_each() { | |||
e.jaxis.axis = IZ_JOY_AXIS_DIRECTION_HORIZONTAL2; | |||
IZ_GameControllerHandleEvents(&state, &action, e); | |||
check(action[p] != 0, "Action not set."); | |||
} | |||
it("handles positive motion") { | |||
e.jaxis.value = GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold); | |||
if (e.jaxis.value < 0) { | |||
e.jaxis.value = -e.jaxis.value; | |||
} | |||
it("handles %s negative motion activation", axis_name) { | |||
e.type = SDL_CONTROLLERAXISMOTION; | |||
e.caxis.axis = i; | |||
e.caxis.value = -(IZ_GAME_CONTROLLER_AXIS_THRESHOLD + 1); | |||
sprintf(state[p].config.control_mapping[0], "axis:-%s", axis_name); | |||
action[p] = 0; | |||
printf("(axis value: %d) ", e.jaxis.value); | |||
IZ_JoystickHandleEvents(&state, &action, e); | |||
check( | |||
action[p] == (0x1 << IZ_ACTION_INDEX_RIGHT), | |||
"Action not set." | |||
); | |||
IZ_GameControllerHandleEvents(&state, &action, e); | |||
check(action[p] != 0, "Action not set."); | |||
} | |||
it("handles negative motion") { | |||
e.jaxis.value = -GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold); | |||
if (e.jaxis.value > 0) { | |||
e.jaxis.value = -e.jaxis.value; | |||
} | |||
action[p] = 0; | |||
printf("(axis value: %d) ", e.jaxis.value); | |||
IZ_JoystickHandleEvents(&state, &action, e); | |||
check( | |||
action[p] == (0x1 << IZ_ACTION_INDEX_LEFT), | |||
"Action not set." | |||
); | |||
it("handles %s positive motion deactivation", axis_name) { | |||
e.type = SDL_CONTROLLERAXISMOTION; | |||
e.caxis.axis = i; | |||
e.caxis.value = IZ_GAME_CONTROLLER_AXIS_THRESHOLD - 1; | |||
sprintf(state[p].config.control_mapping[0], "axis:+%s", axis_name); | |||
action[p] = 1; | |||
IZ_GameControllerHandleEvents(&state, &action, e); | |||
check(action[p] != 1, "Action not unset. %d", action[p]); | |||
} | |||
it("handles neutral motion") { | |||
e.jaxis.value = GenerateAxisValueWithinThreshold(state[p].config.axis_threshold);; | |||
action[p] = 0; | |||
printf("(axis value: %d) ", e.jaxis.value); | |||
IZ_JoystickHandleEvents(&state, &action, e); | |||
check( | |||
action[p] == 0, | |||
"Action not set." | |||
); | |||
} | |||
} | |||
describe("on primary vertical direction") { | |||
before_each() { | |||
e.jaxis.axis = IZ_JOY_AXIS_DIRECTION_VERTICAL1; | |||
} | |||
it("handles positive motion") { | |||
e.jaxis.value = GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold); | |||
if (e.jaxis.value < 0) { | |||
e.jaxis.value = -e.jaxis.value; | |||
} | |||
action[p] = 0; | |||
printf("(axis value: %d) ", e.jaxis.value); | |||
IZ_JoystickHandleEvents(&state, &action, e); | |||
check( | |||
action[p] == (0x1 << IZ_ACTION_INDEX_DOWN), | |||
"Action not set." | |||
); | |||
} | |||
it("handles negative motion") { | |||
e.jaxis.value = -GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold); | |||
if (e.jaxis.value > 0) { | |||
e.jaxis.value = -e.jaxis.value; | |||
} | |||
action[p] = 0; | |||
printf("(axis value: %d) ", e.jaxis.value); | |||
IZ_JoystickHandleEvents(&state, &action, e); | |||
check( | |||
action[p] == (0x1 << IZ_ACTION_INDEX_UP), | |||
"Action not set." | |||
); | |||
} | |||
it("handles neutral motion") { | |||
e.jaxis.value = GenerateAxisValueWithinThreshold(state[p].config.axis_threshold);; | |||
action[p] = 0; | |||
printf("(axis value: %d) ", e.jaxis.value); | |||
IZ_JoystickHandleEvents(&state, &action, e); | |||
check( | |||
action[p] == 0, | |||
"Action not set." | |||
); | |||
} | |||
} | |||
describe("on secondary vertical direction") { | |||
before_each() { | |||
e.jaxis.axis = IZ_JOY_AXIS_DIRECTION_VERTICAL2; | |||
} | |||
it("handles positive motion") { | |||
e.jaxis.value = GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold); | |||
if (e.jaxis.value < 0) { | |||
e.jaxis.value = -e.jaxis.value; | |||
} | |||
action[p] = 0; | |||
printf("(axis value: %d) ", e.jaxis.value); | |||
IZ_JoystickHandleEvents(&state, &action, e); | |||
check( | |||
action[p] == (0x1 << IZ_ACTION_INDEX_DOWN), | |||
"Action not set." | |||
); | |||
} | |||
it("handles negative motion") { | |||
e.jaxis.value = -GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold); | |||
if (e.jaxis.value > 0) { | |||
e.jaxis.value = -e.jaxis.value; | |||
} | |||
action[p] = 0; | |||
printf("(axis value: %d) ", e.jaxis.value); | |||
IZ_JoystickHandleEvents(&state, &action, e); | |||
check( | |||
action[p] == (0x1 << IZ_ACTION_INDEX_UP), | |||
"Action not set." | |||
); | |||
} | |||
it("handles neutral motion") { | |||
e.jaxis.value = GenerateAxisValueWithinThreshold(state[p].config.axis_threshold);; | |||
action[p] = 0; | |||
printf("(axis value: %d) ", e.jaxis.value); | |||
IZ_JoystickHandleEvents(&state, &action, e); | |||
check( | |||
action[p] == 0, | |||
"Action not set." | |||
); | |||
} | |||
} | |||
} | |||
describe("on hat motion events") { | |||
before_each() { | |||
e.type = SDL_JOYHATMOTION; | |||
} | |||
for (u8 i = 0; i < 4; i += 1) { | |||
it("handles motion for %s action", IZ_ACTION_NAMES[i]) { | |||
e.jhat.value = (0x1u << i); | |||
action[p] = 0; | |||
IZ_JoystickHandleEvents(&state, &action, e); | |||
check( | |||
action[p] == (0x1u << i), | |||
"Action not set." | |||
); | |||
} | |||
it("handles motion for %s deactivation", IZ_ACTION_NAMES[i]) { | |||
e.jhat.value = 0; | |||
action[p] = ~0; | |||
IZ_JoystickHandleEvents(&state, &action, e); | |||
check( | |||
!(action[p] & (0x1 << i)), | |||
"Action not unset." | |||
); | |||
it("handles %s negative motion deactivation", axis_name) { | |||
e.type = SDL_CONTROLLERAXISMOTION; | |||
e.caxis.axis = i; | |||
e.caxis.value = -(IZ_GAME_CONTROLLER_AXIS_THRESHOLD - 1); | |||
sprintf(state[p].config.control_mapping[0], "axis:-%s", axis_name); | |||
action[p] = 1; | |||
IZ_GameControllerHandleEvents(&state, &action, e); | |||
check(action[p] != 1, "Action not unset. %d", action[p]); | |||
} | |||
} | |||
} | |||
describe("on button events") { | |||
for (u8 i = 4; i < IZ_CONTROLS; i += 1) { | |||
it("handles %s action activation", IZ_ACTION_NAMES[i]) { | |||
e.type = SDL_JOYBUTTONDOWN; | |||
e.jbutton.button = IZ_JOYSTICK_DEFAULT_STATE[p].config.control_mapping[i]; | |||
state[p].config.control_mapping[i] = IZ_JOYSTICK_DEFAULT_STATE[p].config.control_mapping[i]; | |||
for (u8 i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i += 1) { | |||
static const char* button_name; | |||
button_name = SDL_GameControllerGetStringForButton(i); | |||
it("handles %s action activation", button_name) { | |||
e.type = SDL_CONTROLLERBUTTONDOWN; | |||
e.cbutton.button = i; | |||
sprintf(state[p].config.control_mapping[0], "button:%s", button_name); | |||
action[p] = 0; | |||
if (i == IZ_ACTION_INDEX_ACTION2) { | |||
; | |||
} | |||
if (i == IZ_ACTION_INDEX_ACTION4) { | |||
; | |||
} | |||
IZ_JoystickHandleEvents(&state, &action, e); | |||
check( | |||
action[p] == (0x1u << i), | |||
"Action not set." | |||
); | |||
IZ_GameControllerHandleEvents(&state, &action, e); | |||
check(action[p] != 0, "Action not set."); | |||
} | |||
it("handles %s action deactivation", IZ_ACTION_NAMES[i]) { | |||
e.type = SDL_JOYBUTTONUP; | |||
e.jbutton.button = IZ_JOYSTICK_DEFAULT_STATE[p].config.control_mapping[i]; | |||
state[p].config.control_mapping[i] = IZ_JOYSTICK_DEFAULT_STATE[p].config.control_mapping[i]; | |||
action[p] = ~0; | |||
IZ_JoystickHandleEvents(&state, &action, e); | |||
check( | |||
!(action[p] & (0x1 << i)), | |||
"Action not unset." | |||
); | |||
it("handles %s action deactivation", button_name) { | |||
e.type = SDL_CONTROLLERBUTTONUP; | |||
e.cbutton.button = i; | |||
sprintf(state[p].config.control_mapping[0], "button:%s", button_name); | |||
action[p] = 1; | |||
IZ_GameControllerHandleEvents(&state, &action, e); | |||
check(action[p] != 1, "Action not unset."); | |||
} | |||
} | |||
} | |||
@@ -359,7 +160,7 @@ spec("input") { | |||
} | |||
describe("SaveConfig") { | |||
static IZ_JoystickState state[IZ_PLAYERS]; | |||
static IZ_GameControllerState state[IZ_PLAYERS]; | |||
after_each() { | |||
mock_reset(INI_ConfigSave); | |||
@@ -368,13 +169,18 @@ spec("input") { | |||
before_each() { | |||
for (u8 p = 0; p < IZ_PLAYERS; p += 1) { | |||
for (u8 i = 0; i < IZ_CONTROLS; i += 1) { | |||
state[p].config.control_mapping[i] = IZ_JOYSTICK_DEFAULT_STATE[p].config.control_mapping[i]; | |||
IZ_memcpy( | |||
state[p].config.control_mapping[i], | |||
IZ_GAME_CONTROLLER_MAX_CONTROL_NAME_LENGTH, | |||
IZ_GAME_CONTROLLER_DEFAULT_STATE[p].config.control_mapping[i], | |||
IZ_GAME_CONTROLLER_MAX_CONTROL_NAME_LENGTH | |||
); | |||
} | |||
} | |||
} | |||
it("calls save method") { | |||
IZ_JoystickSaveConfig(&state, IZ_CONFIG_GAME_PATH); | |||
IZ_GameControllerSaveConfig(&state, IZ_CONFIG_GAME_PATH); | |||
check(mock_is_called(INI_ConfigSave), "Config save function not called."); | |||
} | |||
@@ -382,7 +188,7 @@ spec("input") { | |||
describe("Teardown") { | |||
static void* device = (void*) 1; | |||
static IZ_JoystickState state[IZ_PLAYERS] = {}; | |||
static IZ_GameControllerState state[IZ_PLAYERS] = {}; | |||
before_each() { | |||
for (u8 p = 0; p < IZ_PLAYERS; p += 1) { | |||
@@ -391,19 +197,19 @@ spec("input") { | |||
} | |||
after_each() { | |||
mock_reset(SDL_JoystickClose); | |||
mock_reset(SDL_GameControllerClose); | |||
} | |||
it("closes opened devices") { | |||
mock_set_expected_calls(SDL_JoystickClose, IZ_PLAYERS); | |||
mock_set_expected_calls(SDL_GameControllerClose, IZ_PLAYERS); | |||
IZ_JoystickTeardown(&state); | |||
IZ_GameControllerTeardown(&state); | |||
check( | |||
mock_get_expected_calls(SDL_JoystickClose) == mock_get_actual_calls(SDL_JoystickClose), | |||
"Call count mismatch for SDL_JoystickClose() (expected %u, received %u).", | |||
mock_get_expected_calls(SDL_JoystickClose), | |||
mock_get_actual_calls(SDL_JoystickClose) | |||
mock_get_expected_calls(SDL_GameControllerClose) == mock_get_actual_calls(SDL_GameControllerClose), | |||
"Call count mismatch for SDL_GameControllerClose() (expected %u, received %u).", | |||
mock_get_expected_calls(SDL_GameControllerClose), | |||
mock_get_actual_calls(SDL_GameControllerClose) | |||
); | |||
} | |||
} | |||
@@ -1,20 +1,45 @@ | |||
# Controls | |||
| Action | XInput | PS4 | Effect | Remarks | | |||
|---------|---------:|---------:|-----------------------------------|--------------------------------------------------------------------------------------------------------| | |||
| Up | A1+, A3+ | A1+, A4+ | Select Menu Item Up/Look Up | | | |||
| Right | A0+, A2+ | A0+, A2+ | Select Menu Item Right/Move Right | | | |||
| Down | A1-, A3- | A1-, A4- | Select Menu Item Down/Crouch | | | |||
| Left | A0-, A2- | A0-, A2- | Select Menu Item Left/Move Left | | | |||
| Affirm | B11 | | Confirm Selected Option | | | |||
| Negate | B10 | | Go Back | | | |||
| Action0 | B1 | B2 | Jump | When crouched, player will go down a solid-on-top ground. | | |||
| Action1 | B0 | B1 | Primary Fire | Each weapon has firing characteristics, see below. | | |||
| Action2 | B4 | | Secondary Fire | Each weapon has firing characteristics, see below. | | |||
| Action3 | B3 | | Reload | Reload always drops current clip regardless if there are rounds left. | | |||
| Action4 | B6 | | Switch Weapon Mode | Press to select next weapon mode (cycle), hold then press Left/Right to set previous/next weapon mode. | | |||
| Action5 | B7 | | Switch Weapon | Press to select next weapon (cycle), hold then press Left/Right to set previous/next weapon. | | |||
| Action6 | B8 | | Item/Inventory | Hold to open inventory, double press to use current item. | | |||
| Action7 | B9 | | Switch Item | Press to select next item (cycle), hold then press Left/Right to set previous/next item. | | |||
| Action8 | B13 | | Take Cover | Enter doors, go between objects to hide from enemies. | | |||
| Action9 | B14 | | Sneak | Hold then press Left/Right to sneak. (do we need this control, or crouch is enough?) | | |||
| Action | Default Control | Effect | Remarks | | |||
|---------|------------------------|-----------------------------------|--------------------------------------------------------------------------------------------------------| | |||
| Up | DPad Up/Stick Up | Select Menu Item Up/Look Up | | | |||
| Right | DPad Right/Stick Right | Select Menu Item Right/Move Right | | | |||
| Down | DPad Down/Stick Down | Select Menu Item Down/Crouch | | | |||
| Left | DPad Left/Stick Left | Select Menu Item Left/Move Left | | | |||
| Affirm | Start | Confirm Selected Option | | | |||
| Negate | Select (Back) | Go Back | | | |||
| Action0 | Button X | Jump | When crouched, player will go down a solid-on-top ground. | | |||
| Action1 | Right Trigger | Primary Fire | Each weapon has firing characteristics, see below. | | |||
| Action2 | Left Trigger | Secondary Fire | Each weapon has firing characteristics, see below. | | |||
| Action3 | Button A | Reload | Reload always drops current clip regardless if there are rounds left. | | |||
| Action4 | Button Y | Switch Weapon Mode | Press to select next weapon mode (cycle), hold then press Left/Right to set previous/next weapon mode. | | |||
| Action5 | Right Bumper/Shoulder | Switch Weapon | Press to select next weapon (cycle), hold then press Left/Right to set previous/next weapon. | | |||
| Action6 | Left Stick Button | Item/Inventory | Hold to open inventory, double press to use current item. | | |||
| Action7 | Right Stick Button | Switch Item | Press to select next item (cycle), hold then press Left/Right to set previous/next item. | | |||
| Action8 | Left Bumper/Shoulder | Take Cover | Enter doors, go between objects to hide from enemies. | | |||
| Action9 | Button B | Sneak | Hold then press Left/Right to sneak. (do we need this control, or crouch is enough?) | | |||
# Control Internals | |||
| Control | Config Key | | |||
|-----------------------|------------------------| | |||
| DPad Up | `button:dpup` | | |||
| DPad Right | `button:dpright` | | |||
| DPad Down | `button:dpdown` | | |||
| DPad Left | `button:dpleft` | | |||
| Start | `button:start` | | |||
| Select (Back) | `button:back` | | |||
| Button A | `button:a` | | |||
| Button B | `button:b` | | |||
| Button X | `button:x` | | |||
| Button Y | `button:y` | | |||
| Right Bumper/Shoulder | `button:rightshoulder` | | |||
| Right Trigger | `axis:+righttrigger` | | |||
| Left Stick Button | `button:leftstick` | | |||
| Right Stick Button | `button:rightstick` | | |||
| Left Bumper/Shoulder | `button:leftshoulder` | | |||
| Left Trigger | `axis:lefttrigger` | | |||
> **Note:** The config keys are based from SDL internal names, only we have added the `button:` and `axis:` bind | |||
> prefixes to be flexible on setting up controls on the gamepad. The reason is because gamepad controls use different | |||
> types of binds. |
@@ -26,7 +26,7 @@ void INI_ConfigEnsureValidGuid(INI_ConfigItem* item, SDL_GUID raw_value, SDL_GUI | |||
*dest = raw_value; | |||
} | |||
void INI_ConfigLoadGuid(INI_ConfigItem* item, const char* config_path) { | |||
void INI_ConfigLoadGuid(INI_ConfigItem* item, const char* config_path, void* item_array) { | |||
static SDL_GUID raw_value; | |||
static SDL_GUID default_value = { | |||
.data = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, | |||
@@ -53,7 +53,7 @@ void INI_ConfigLoadGuid(INI_ConfigItem* item, const char* config_path) { | |||
INI_ConfigEnsureValidGuid(item, raw_value, default_value); | |||
} | |||
INI_ConfigSaveItemResult INI_ConfigSaveGuid(INI_ConfigItem* item, const char* config_path) { | |||
INI_ConfigSaveItemResult INI_ConfigSaveGuid(INI_ConfigItem* item, const char* config_path, void* item_array) { | |||
SDL_GUID dest = *((SDL_GUID*) item->dest); | |||
if (item->validator) { | |||
INI_ConfigLoadParamsGuidValidator* validate = item->validator; | |||
@@ -0,0 +1,272 @@ | |||
#include "IZ_gamecontroller.h" | |||
static INI_ConfigItem game_controller_config_items[IZ_PLAYERS * (IZ_CONTROLS + IZ_GAME_CONTROLLER_DEFAULT_CONFIGS) + 1]; | |||
bool IZ_GameControllerIsValidAxisThreshold(u16 value) { | |||
return (IZ_GAME_CONTROLLER_MIN_AXIS_THRESHOLD <= value && value <= IZ_GAME_CONTROLLER_MAX_AXIS_THRESHOLD); | |||
} | |||
void IZ_GameControllerHandleDeviceEvents(IZ_GameControllerState* state, SDL_Event e) { | |||
if (e.type == SDL_CONTROLLERDEVICEADDED) { | |||
u8 game_controllers_count = SDL_NumJoysticks(); | |||
if (game_controllers_count <= IZ_PLAYERS && !state->device) { | |||
state->device = SDL_GameControllerOpen(e.cdevice.which); | |||
} | |||
return; | |||
} | |||
if (e.type == SDL_CONTROLLERDEVICEREMOVED) { | |||
if ( | |||
state->device | |||
&& SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(state->device)) == e.cdevice.which | |||
) { | |||
state->device = NULL; | |||
} | |||
} | |||
} | |||
void IZ_GameControllerHandleAxisEvents(IZ_GameControllerState* state, IZ_Action* action, SDL_Event e) { | |||
if (e.type != SDL_CONTROLLERAXISMOTION) { | |||
return; | |||
} | |||
u8 control_index; | |||
for (control_index = 0; control_index < IZ_CONTROLS; control_index += 1) { | |||
const char* current_axis_name = SDL_GameControllerGetStringForAxis(e.caxis.axis); | |||
if (!current_axis_name) { | |||
continue; | |||
} | |||
char current_positive_axis_full_name[IZ_GAME_CONTROLLER_MAX_CONTROL_NAME_LENGTH]; | |||
sprintf( | |||
current_positive_axis_full_name, | |||
"axis:+%s", | |||
current_axis_name | |||
); | |||
char current_negative_axis_full_name[IZ_GAME_CONTROLLER_MAX_CONTROL_NAME_LENGTH]; | |||
sprintf( | |||
current_negative_axis_full_name, | |||
"axis:-%s", | |||
current_axis_name | |||
); | |||
bool is_capture = false; | |||
const u16 bitflag = (0x1 << control_index); | |||
if ( | |||
strstr(state->config.control_mapping[control_index], current_positive_axis_full_name) | |||
|| strstr(state->config.control_mapping[control_index], current_negative_axis_full_name) | |||
) { | |||
*action &= ~bitflag; | |||
// should we implement actions that do not cancel out across input controllers? | |||
// add extra byte for source of action: | |||
// 0x1 - keyboard | |||
// 0x2 - game controller dpad | |||
// 0x4 - game controller left stick | |||
// 0x8 - game controller right stick | |||
// 0x10 - other device | |||
} | |||
char current_axis_full_name[IZ_GAME_CONTROLLER_MAX_CONTROL_NAME_LENGTH]; | |||
if (e.caxis.value > 0) { | |||
IZ_memcpy( | |||
current_axis_full_name, | |||
IZ_GAME_CONTROLLER_MAX_CONTROL_NAME_LENGTH, | |||
current_positive_axis_full_name, | |||
IZ_GAME_CONTROLLER_MAX_CONTROL_NAME_LENGTH | |||
); | |||
is_capture = e.caxis.value > state->config.axis_threshold; | |||
} | |||
if (e.caxis.value < 0) { | |||
IZ_memcpy( | |||
current_axis_full_name, | |||
IZ_GAME_CONTROLLER_MAX_CONTROL_NAME_LENGTH, | |||
current_negative_axis_full_name, | |||
IZ_GAME_CONTROLLER_MAX_CONTROL_NAME_LENGTH | |||
); | |||
is_capture = e.jaxis.value <= -state->config.axis_threshold; | |||
} | |||
if (!strstr(state->config.control_mapping[control_index], current_axis_full_name)) { | |||
continue; | |||
} | |||
if (!is_capture) { | |||
continue; | |||
} | |||
*action |= bitflag; | |||
} | |||
} | |||
void IZ_GameControllerHandleButtonEvents(IZ_GameControllerState* state, IZ_Action* action, SDL_Event e) { | |||
u8 control_index; | |||
for (control_index = 0; control_index < IZ_CONTROLS; control_index += 1) { | |||
const char* current_button_name = SDL_GameControllerGetStringForButton(e.cbutton.button); | |||
if (!current_button_name) { | |||
continue; | |||
} | |||
char current_button_full_name[IZ_GAME_CONTROLLER_MAX_CONTROL_NAME_LENGTH]; | |||
sprintf( | |||
current_button_full_name, | |||
"button:%s", | |||
current_button_name | |||
); | |||
if (!strstr(state->config.control_mapping[control_index], current_button_full_name)) { | |||
continue; | |||
} | |||
const u16 bitflag = (0x1 << control_index); | |||
if (e.type == SDL_CONTROLLERBUTTONDOWN) { | |||
*action |= bitflag; | |||
return; | |||
} | |||
if (e.type == SDL_CONTROLLERBUTTONUP) { | |||
*action &= ~bitflag; | |||
return; | |||
} | |||
} | |||
} | |||
void IZ_GameControllerHandleEvents(IZ_GameControllerState(* state)[IZ_PLAYERS], IZ_Action(* action)[IZ_PLAYERS], SDL_Event e) { | |||
u8 player_index; | |||
for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) { | |||
IZ_GameControllerHandleDeviceEvents(&(*state)[player_index], e); | |||
IZ_GameControllerHandleAxisEvents(&(*state)[player_index], &(*action)[player_index], e); | |||
IZ_GameControllerHandleButtonEvents(&(*state)[player_index], &(*action)[player_index], e); | |||
} | |||
} | |||
void IZ_GameControllerBindStateToConfig(IZ_GameControllerState(* state)[IZ_PLAYERS], INI_ConfigItem config_items[]) { | |||
u8 player_index; | |||
u8 control_index; | |||
for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) { | |||
// The `+ 3` corresponds to the first three config items. Add as appropriate. | |||
u8 base_index = (player_index * (IZ_CONTROLS + IZ_GAME_CONTROLLER_DEFAULT_CONFIGS)); | |||
config_items[base_index + 0].dest = &((*state)[player_index].config.axis_threshold); | |||
config_items[base_index + 1].dest = &((*state)[player_index].config.guid); | |||
for (control_index = 0; control_index < IZ_CONTROLS; control_index += 1) { | |||
config_items[base_index + IZ_GAME_CONTROLLER_DEFAULT_CONFIGS + control_index].dest = &((*state)[player_index].config.control_mapping[control_index]); | |||
} | |||
} | |||
} | |||
IZ_ProcedureResult IZ_GameControllerSaveConfig(IZ_GameControllerState(* state)[IZ_PLAYERS], const char* config_path) { | |||
IZ_GameControllerBindStateToConfig(state, game_controller_config_items); | |||
return INI_ConfigSave(game_controller_config_items, config_path); | |||
} | |||
void IZ_GameControllerInitializeConfigItems(INI_ConfigItem config_items[]) { | |||
u8 player_index; | |||
u8 control_index; | |||
char* main_section_name; | |||
char* control_mapping_section_name; | |||
for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) { | |||
main_section_name = IZ_calloc(64, sizeof(char)); | |||
sprintf(main_section_name, "GameController.%d", player_index); | |||
// The `+ 3` corresponds to the first three config items. Add as appropriate. | |||
u8 base_index = (player_index * (IZ_CONTROLS + IZ_GAME_CONTROLLER_DEFAULT_CONFIGS)); | |||
config_items[base_index + 0] = (INI_ConfigItem) { | |||
INI_CONFIG_TYPE_U16, | |||
main_section_name, | |||
"AxisThreshold", | |||
NULL, | |||
&IZ_GAME_CONTROLLER_DEFAULT_STATE[player_index].config.axis_threshold, | |||
IZ_GameControllerIsValidAxisThreshold, | |||
INI_CONFIG_TRANSFORMER_NONE, | |||
NULL, | |||
}; | |||
config_items[base_index + 1] = (INI_ConfigItem) { | |||
INI_CONFIG_TYPE_GUID, | |||
main_section_name, | |||
"GUID", | |||
NULL, | |||
&IZ_GAME_CONTROLLER_DEFAULT_STATE[player_index].config.guid, | |||
NULL, | |||
INI_CONFIG_TRANSFORMER_NONE, | |||
NULL, | |||
}; | |||
control_mapping_section_name = IZ_calloc(64, sizeof(char)); | |||
sprintf(control_mapping_section_name, "GameController.%d.ControlMapping", player_index); | |||
for (control_index = 0; control_index < IZ_CONTROLS; control_index += 1) { | |||
config_items[base_index + IZ_GAME_CONTROLLER_DEFAULT_CONFIGS + control_index] = (INI_ConfigItem) { | |||
INI_CONFIG_TYPE_STRING(IZ_GAME_CONTROLLER_MAX_CONTROL_NAME_LENGTH), | |||
control_mapping_section_name, | |||
IZ_ACTION_NAMES[control_index], | |||
NULL, | |||
&IZ_GAME_CONTROLLER_DEFAULT_STATE[player_index].config.control_mapping[control_index], | |||
NULL, | |||
INI_CONFIG_TRANSFORMER_NONE, | |||
NULL, | |||
}; | |||
} | |||
} | |||
config_items[IZ_PLAYERS * (IZ_CONTROLS + IZ_GAME_CONTROLLER_DEFAULT_CONFIGS)] = INI_CONFIG_ITEM_NULL; | |||
} | |||
IZ_ProcedureResult IZ_GameControllerInitializeConfig(IZ_GameControllerState(* state)[IZ_PLAYERS], const char* config_path, u8 argc, const char* argv[]) { | |||
IZ_GameControllerInitializeConfigItems(game_controller_config_items); | |||
IZ_GameControllerBindStateToConfig(state, game_controller_config_items); | |||
if (INI_ConfigInitialize(game_controller_config_items, config_path, argc, argv) < 0) { | |||
return -1; | |||
} | |||
return 0; | |||
} | |||
IZ_ProcedureResult IZ_GameControllerInitialize(IZ_GameControllerState(* state)[IZ_PLAYERS], const char* config_path, u8 argc, const char* argv[]) { | |||
IZ_memcpy(state, sizeof(IZ_GameControllerState) * IZ_PLAYERS, &IZ_GAME_CONTROLLER_DEFAULT_STATE, sizeof(IZ_GameControllerState) * IZ_PLAYERS); | |||
// TODO query this file from the internet? | |||
IZ_LogInfo(IZ_LOG_CATEGORY_GENERIC, "input", "Loading game controller mappings file: %s", "assets/gamecontrollerdb.txt"); | |||
const i16 loaded_mappings = SDL_GameControllerAddMappingsFromFile("assets/gamecontrollerdb.txt"); | |||
if (loaded_mappings <= 0) { | |||
return -1; | |||
} | |||
IZ_LogInfo(IZ_LOG_CATEGORY_GENERIC, "input", "Mappings loaded: %d", loaded_mappings); | |||
u8 player_index; | |||
if (IZ_GameControllerInitializeConfig(state, config_path, argc, argv) < 0) { | |||
return -2; | |||
} | |||
u8 game_controllers_count = SDL_NumJoysticks(); | |||
for (player_index = 0; player_index < game_controllers_count; player_index += 1) { | |||
if (player_index >= IZ_PLAYERS) { | |||
break; | |||
} | |||
(*state)[player_index].device = SDL_GameControllerOpen(state[player_index]->config.device_id); | |||
if (!(*state)[player_index].device) { | |||
break; | |||
} | |||
(*state)[player_index].config.device_id = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick((*state)[player_index].device)); | |||
SDL_GUID game_controller_guid = SDL_JoystickGetGUID(SDL_GameControllerGetJoystick((*state)[player_index].device)); | |||
IZ_memcpy(&(*state)[player_index].config.guid, sizeof(SDL_GUID), &game_controller_guid, sizeof(SDL_GUID)); | |||
} | |||
// Post config (after game_controller GUIDs have been queried), this is unique to game_controllers since they can be plugged in any | |||
// time. | |||
INI_ConfigSaveResult post_config_save_result = IZ_GameControllerSaveConfig(state, config_path); | |||
if (post_config_save_result < 0) { | |||
return -3; | |||
} | |||
return 0; | |||
} | |||
void IZ_GameControllerTeardown(IZ_GameControllerState(* state)[IZ_PLAYERS]) { | |||
u8 player_index; | |||
for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) { | |||
if (!(*state)[player_index].device) { | |||
continue; | |||
} | |||
SDL_GameControllerClose((*state)[player_index].device); | |||
} | |||
} |
@@ -0,0 +1,104 @@ | |||
#ifndef IZ_GAMECONTROLLER_H | |||
#define IZ_GAMECONTROLLER_H | |||
#include <SDL_gamecontroller.h> | |||
#include <SDL_events.h> | |||
#include <ini-config.h> | |||
#include <ini-config/source/types/int.h> | |||
#include <ini-config/source/types/string.h> | |||
#include "../../config/IZ_config_guid.h" | |||
#include "../../common/IZ_common.h" | |||
#include "../../stdinc/IZ_string.h" | |||
#include "../../stdinc/IZ_stdlib.h" | |||
#include "../../log/IZ_log.h" | |||
#include "IZ_action.h" | |||
#define IZ_GAME_CONTROLLER_AXIS_THRESHOLD ((u16) 8000) | |||
#define IZ_GAME_CONTROLLER_MIN_AXIS_THRESHOLD ((u16) 4000) | |||
#define IZ_GAME_CONTROLLER_MAX_AXIS_THRESHOLD ((u16) 12000) | |||
#define IZ_GAME_CONTROLLER_DEFAULT_CONFIGS 2 | |||
#define IZ_GAME_CONTROLLER_MAX_CONTROL_NAME_LENGTH 64 | |||
typedef struct { | |||
u16 axis_threshold; | |||
SDL_JoystickID device_id; | |||
SDL_GUID guid; | |||
const char control_mapping[IZ_CONTROLS][IZ_GAME_CONTROLLER_MAX_CONTROL_NAME_LENGTH]; | |||
} IZ_GameControllerConfig; | |||
typedef struct { | |||
SDL_GameController* device; | |||
IZ_GameControllerConfig config; | |||
} IZ_GameControllerState; | |||
static const IZ_GameControllerState IZ_GAME_CONTROLLER_DEFAULT_STATE[IZ_PLAYERS] = { | |||
{ | |||
.config = { | |||
.control_mapping = { | |||
"button:dpup axis:+lefty axis:+righty", | |||
"button:dpright axis:+leftx axis:+rightx", | |||
"button:dpdown axis:-lefty axis:-righty", | |||
"button:dpleft axis:-leftx axis:-rightx", | |||
"button:start", | |||
"button:back", | |||
"button:x", | |||
"axis:+righttrigger", | |||
"axis:+lefttrigger", | |||
"button:a", | |||
"button:y", | |||
"button:rightshoulder", | |||
"button:leftstick", | |||
"button:rightstick", | |||
"button:leftshoulder", | |||
"button:b", | |||
}, | |||
.axis_threshold = IZ_GAME_CONTROLLER_AXIS_THRESHOLD, | |||
.guid = { | |||
.data = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } | |||
}, | |||
}, | |||
.device = NULL, | |||
}, | |||
#if IZ_PLAYERS > 1 | |||
{ | |||
.config = { | |||
.control_mapping = { | |||
"button:dpadup", | |||
"button:dpadright", | |||
"button:dpaddown", | |||
"button:dpadleft", | |||
"button:start", | |||
"button:back", | |||
"button:x", | |||
"button:y", | |||
"button:a", | |||
"button:b", | |||
"button:rightshoulder", | |||
"axis:righttrigger", | |||
"button:leftstick", | |||
"button:rightstick", | |||
"button:leftshoulder", | |||
"axis:lefttrigger", | |||
}, | |||
.axis_threshold = 8000u, | |||
.guid = { | |||
.data = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } | |||
}, | |||
}, | |||
.device = NULL, | |||
}, | |||
#endif | |||
}; | |||
IZ_ProcedureResult IZ_GameControllerSaveConfig(IZ_GameControllerState(*)[IZ_PLAYERS], const char*); | |||
void IZ_GameControllerHandleEvents(IZ_GameControllerState(*)[IZ_PLAYERS], IZ_Action(*)[IZ_PLAYERS], SDL_Event); | |||
IZ_ProcedureResult IZ_GameControllerInitialize(IZ_GameControllerState(*)[IZ_PLAYERS], const char*, u8, const char*[]); | |||
void IZ_GameControllerTeardown(IZ_GameControllerState(*)[IZ_PLAYERS]); | |||
#endif |
@@ -1,7 +1,7 @@ | |||
#include "IZ_input.h" | |||
void IZ_InputHandleSDLEvents(IZ_InputState* state, SDL_Event e) { | |||
IZ_JoystickHandleEvents(&state->joystick_state, &state->action, e); | |||
IZ_GameControllerHandleEvents(&state->game_controller_state, &state->action, e); | |||
IZ_KeyboardHandleEvents(&state->keyboard_state, &state->action, e); | |||
} | |||
@@ -14,14 +14,14 @@ IZ_ProcedureResult IZ_InputInitialize(IZ_InputState* state, const char* config_p | |||
*state = (IZ_InputState) { | |||
.action = {}, | |||
.joystick_state = {}, | |||
.game_controller_state = {}, | |||
.midi_input_state = {}, | |||
.keyboard_state = {}, | |||
}; | |||
IZ_ProcedureResult result = 0; | |||
if (IZ_JoystickInitialize(&state->joystick_state, config_path, argc, argv) < 0) { | |||
if (IZ_GameControllerInitialize(&state->game_controller_state, config_path, argc, argv) < 0) { | |||
result |= 1; | |||
} | |||
@@ -47,6 +47,6 @@ IZ_ProcedureResult IZ_InputInitialize(IZ_InputState* state, const char* config_p | |||
void IZ_InputTeardown(IZ_InputState* state) { | |||
IZ_LogInfo(IZ_LOG_CATEGORY_GLOBAL, "input", "Shutting down..."); | |||
IZ_JoystickTeardown(&state->joystick_state); | |||
IZ_GameControllerTeardown(&state->game_controller_state); | |||
IZ_MIDIInputTeardown(&state->midi_input_state); | |||
} |
@@ -3,14 +3,14 @@ | |||
#include "IZ_action.h" | |||
#include "IZ_keyboard.h" | |||
#include "IZ_joystick.h" | |||
#include "IZ_gamecontroller.h" | |||
#include "IZ_midi.h" | |||
#include "../../log/IZ_log.h" | |||
typedef struct { | |||
IZ_Action action[IZ_PLAYERS]; | |||
IZ_KeyboardState keyboard_state[IZ_PLAYERS]; | |||
IZ_JoystickState joystick_state[IZ_PLAYERS]; | |||
IZ_GameControllerState game_controller_state[IZ_PLAYERS]; | |||
IZ_MIDIInputState midi_input_state[IZ_PLAYERS]; | |||
} IZ_InputState; | |||
@@ -1,324 +0,0 @@ | |||
#include "IZ_joystick.h" | |||
static INI_ConfigItem joystick_config_items[IZ_PLAYERS * (IZ_CONTROLS - 4 + 3) + 1]; | |||
bool IZ_JoystickIsValidAxisThreshold(u16 value) { | |||
return (4000 <= value && value <= 12000); | |||
} | |||
void IZ_JoystickHandleDeviceEvents(IZ_JoystickState* state, SDL_Event e) { | |||
if (e.type == SDL_JOYDEVICEADDED) { | |||
u8 joysticks_count = SDL_NumJoysticks(); | |||
if (joysticks_count <= IZ_PLAYERS && !state->device) { | |||
state->device = SDL_JoystickOpen(e.jdevice.which); | |||
} | |||
return; | |||
} | |||
if (e.type == SDL_JOYDEVICEREMOVED) { | |||
if ( | |||
state->device | |||
&& SDL_JoystickInstanceID(state->device) == e.jdevice.which | |||
) { | |||
state->device = NULL; | |||
} | |||
} | |||
} | |||
void IZ_JoystickHandleAxisEvents(IZ_JoystickState* state, IZ_Action* action, SDL_Event e) { | |||
if (e.type == SDL_JOYAXISMOTION) { | |||
// XInput handling | |||
u8 control_index; | |||
for (control_index = 4; control_index < IZ_CONTROLS; control_index += 1) { | |||
if (e.jaxis.axis == IZ_JOY_AXIS_DIRECTION_LEFT_SHOULDER && state->config.control_mapping[control_index] == 10) { | |||
const u16 bitflag = (0x1 << control_index); | |||
if (e.jaxis.value > state->config.axis_threshold) { | |||
*action |= bitflag; | |||
return; | |||
} | |||
if (e.jaxis.value <= state->config.axis_threshold) { | |||
*action &= ~bitflag; | |||
return; | |||
} | |||
} | |||
if (e.jaxis.axis == IZ_JOY_AXIS_DIRECTION_RIGHT_SHOULDER && state->config.control_mapping[control_index] == 11) { | |||
const u16 bitflag = (0x1 << control_index); | |||
if (e.jaxis.value > state->config.axis_threshold) { | |||
*action |= bitflag; | |||
return; | |||
} | |||
if (e.jaxis.value <= state->config.axis_threshold) { | |||
*action &= ~bitflag; | |||
return; | |||
} | |||
} | |||
} | |||
if ( | |||
e.jaxis.axis == IZ_JOY_AXIS_DIRECTION_HORIZONTAL1 | |||
|| e.jaxis.axis == IZ_JOY_AXIS_DIRECTION_HORIZONTAL2 | |||
) { | |||
*action &= ~(0x1 << IZ_ACTION_INDEX_RIGHT); | |||
*action &= ~(0x1 << IZ_ACTION_INDEX_LEFT); | |||
if (e.jaxis.value > state->config.axis_threshold) { | |||
*action |= (0x1 << IZ_ACTION_INDEX_RIGHT); | |||
return; | |||
} | |||
if (e.jaxis.value <= -state->config.axis_threshold) { | |||
*action |= (0x1 << IZ_ACTION_INDEX_LEFT); | |||
} | |||
return; | |||
} | |||
if ( | |||
e.jaxis.axis == IZ_JOY_AXIS_DIRECTION_VERTICAL1 | |||
|| e.jaxis.axis == IZ_JOY_AXIS_DIRECTION_VERTICAL2 | |||
) { | |||
*action &= ~(0x1 << IZ_ACTION_INDEX_UP); | |||
*action &= ~(0x1 << IZ_ACTION_INDEX_DOWN); | |||
if (e.jaxis.value > state->config.axis_threshold) { | |||
*action |= (0x1 << IZ_ACTION_INDEX_DOWN); | |||
return; | |||
} | |||
if (e.jaxis.value <= -state->config.axis_threshold) { | |||
*action |= (0x1 << IZ_ACTION_INDEX_UP); | |||
} | |||
} | |||
} | |||
} | |||
void IZ_JoystickHandleHatEvents(IZ_Action* action, SDL_Event e) { | |||
if (e.type == SDL_JOYHATMOTION) { | |||
*action &= ~(0x1 << IZ_ACTION_INDEX_UP); | |||
*action &= ~(0x1 << IZ_ACTION_INDEX_RIGHT); | |||
*action &= ~(0x1 << IZ_ACTION_INDEX_DOWN); | |||
*action &= ~(0x1 << IZ_ACTION_INDEX_LEFT); | |||
if (e.jhat.value != 0) { | |||
*action |= e.jhat.value; | |||
} | |||
} | |||
} | |||
void IZ_JoystickHandleButtonEvents(IZ_JoystickState* state, IZ_Action* action, SDL_Event e) { | |||
u8 control_index; | |||
SDL_JoystickGUID joystick_guid = SDL_JoystickGetGUID(SDL_JoystickFromInstanceID(e.jbutton.which)); | |||
bool is_valid_event = false; | |||
for (u8 zz = 0; zz < 16; zz += 1) { | |||
is_valid_event |= joystick_guid.data[zz] != 0; | |||
} | |||
if (!is_valid_event) { | |||
return; | |||
} | |||
// printf("button event from guid: "); | |||
// for (u8 zz = 0; zz < 16; zz += 1) { | |||
// printf("%02x", joystick_guid.data[zz]); | |||
// } | |||
// printf("\n"); | |||
for (control_index = 4; control_index < IZ_CONTROLS; control_index += 1) { | |||
u8 normalized_button = e.jbutton.button; | |||
/* | |||
* ZL ZR | |||
* L R | |||
* | |||
* U SLCT STRT 3 | |||
* L R 2 0 | |||
* D 1 | |||
* | |||
* | |||
* | |||
*/ | |||
// TODO test with XInput, make compatible with Apple | |||
// TODO honor mapping with controller | |||
#ifdef IZ_MACOS | |||
//printf("%d\n", e.jbutton.button); | |||
// if (e.jbutton.button == 2) { | |||
// normalized_button = 4; | |||
// } else if (e.jbutton.button == 6) { | |||
// normalized_button = 11; | |||
// } else if (e.jbutton.button == 4) { | |||
// normalized_button = 10; | |||
// } | |||
#endif | |||
if (normalized_button == state->config.control_mapping[control_index]) { | |||
const u16 bitflag = (0x1 << control_index); | |||
if (e.type == SDL_JOYBUTTONDOWN) { | |||
*action |= bitflag; | |||
return; | |||
} | |||
if (e.type == SDL_JOYBUTTONUP) { | |||
*action &= ~bitflag; | |||
return; | |||
} | |||
} | |||
} | |||
} | |||
void IZ_JoystickHandleEvents(IZ_JoystickState(* state)[IZ_PLAYERS], IZ_Action(* action)[IZ_PLAYERS], SDL_Event e) { | |||
u8 player_index; | |||
for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) { | |||
IZ_JoystickHandleDeviceEvents(&(*state)[player_index], e); | |||
IZ_JoystickHandleAxisEvents(&(*state)[player_index], &(*action)[player_index], e); | |||
IZ_JoystickHandleHatEvents(&(*action)[player_index], e); | |||
IZ_JoystickHandleButtonEvents(&(*state)[player_index], &(*action)[player_index], e); | |||
} | |||
} | |||
void IZ_JoystickBindStateToConfig(IZ_JoystickState(* state)[IZ_PLAYERS], INI_ConfigItem config_items[]) { | |||
u8 player_index; | |||
u8 control_index; | |||
for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) { | |||
u8 base_index = (player_index * (IZ_CONTROLS - 4 + 2)); | |||
config_items[base_index].dest = &((*state)[player_index].config.device_id); | |||
config_items[base_index + 1].dest = &((*state)[player_index].config.axis_threshold); | |||
config_items[base_index + 2].dest = &((*state)[player_index].config.guid); | |||
for (control_index = 4; control_index < IZ_CONTROLS; control_index += 1) { | |||
config_items[base_index + 3 + (control_index - 4)].dest = &((*state)[player_index].config.control_mapping[control_index]); | |||
} | |||
} | |||
} | |||
IZ_ProcedureResult IZ_JoystickSaveConfig(IZ_JoystickState(* state)[IZ_PLAYERS], const char* config_path) { | |||
IZ_JoystickBindStateToConfig(state, joystick_config_items); | |||
return INI_ConfigSave(joystick_config_items, config_path); | |||
} | |||
void IZ_JoystickInitializeConfigItems(INI_ConfigItem config_items[]) { | |||
u8 player_index; | |||
u8 control_index; | |||
char* main_section_name; | |||
char* control_mapping_section_name; | |||
for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) { | |||
main_section_name = IZ_calloc(64, sizeof(char)); | |||
sprintf(main_section_name, "Joystick.%d", player_index); | |||
// The `+ 3` corresponds to the first three config items. Add as appropriate. | |||
u8 base_index = (player_index * (IZ_CONTROLS - 4 + 3)); | |||
config_items[base_index] = (INI_ConfigItem) { | |||
INI_CONFIG_TYPE_I32, | |||
main_section_name, | |||
"DeviceID", | |||
NULL, | |||
&IZ_JOYSTICK_DEFAULT_STATE[player_index].config.device_id, | |||
NULL, | |||
INI_CONFIG_TRANSFORMER_NONE, | |||
NULL, | |||
}; | |||
config_items[base_index + 1] = (INI_ConfigItem) { | |||
INI_CONFIG_TYPE_U16, | |||
main_section_name, | |||
"AxisThreshold", | |||
NULL, | |||
&IZ_JOYSTICK_DEFAULT_STATE[player_index].config.axis_threshold, | |||
IZ_JoystickIsValidAxisThreshold, | |||
INI_CONFIG_TRANSFORMER_NONE, | |||
NULL, | |||
}; | |||
config_items[base_index + 2] = (INI_ConfigItem) { | |||
INI_CONFIG_TYPE_GUID, | |||
main_section_name, | |||
"GUID", | |||
NULL, | |||
&IZ_JOYSTICK_DEFAULT_STATE[player_index].config.guid, | |||
NULL, | |||
INI_CONFIG_TRANSFORMER_NONE, | |||
NULL, | |||
}; | |||
// todo add game controller GUID for determining mappings | |||
control_mapping_section_name = IZ_calloc(64, sizeof(char)); | |||
sprintf(control_mapping_section_name, "Joystick.%d.ControlMapping", player_index); | |||
for (control_index = 4; control_index < IZ_CONTROLS; control_index += 1) { | |||
config_items[base_index + 3 + (control_index - 4)] = (INI_ConfigItem) { | |||
INI_CONFIG_TYPE_U8, | |||
control_mapping_section_name, | |||
IZ_ACTION_NAMES[control_index], | |||
NULL, | |||
&IZ_JOYSTICK_DEFAULT_STATE[player_index].config.control_mapping[control_index], | |||
NULL, | |||
INI_CONFIG_TRANSFORMER_NONE, | |||
NULL, | |||
}; | |||
} | |||
} | |||
config_items[IZ_PLAYERS * (IZ_CONTROLS - 4 + 3)] = INI_CONFIG_ITEM_NULL; | |||
} | |||
IZ_ProcedureResult IZ_JoystickInitializeConfig(IZ_JoystickState(* state)[IZ_PLAYERS], const char* config_path, u8 argc, const char* argv[]) { | |||
IZ_JoystickInitializeConfigItems(joystick_config_items); | |||
IZ_JoystickBindStateToConfig(state, joystick_config_items); | |||
if (INI_ConfigInitialize(joystick_config_items, config_path, argc, argv) < 0) { | |||
return -1; | |||
} | |||
return 0; | |||
} | |||
IZ_ProcedureResult IZ_JoystickInitialize(IZ_JoystickState(* state)[IZ_PLAYERS], const char* config_path, u8 argc, const char* argv[]) { | |||
IZ_memcpy(state, sizeof(IZ_JoystickState) * IZ_PLAYERS, &IZ_JOYSTICK_DEFAULT_STATE, sizeof(IZ_JoystickState) * IZ_PLAYERS); | |||
// TODO query this file from the internet? | |||
SDL_GameControllerAddMappingsFromFile("assets/gamecontrollerdb.txt"); | |||
u8 player_index; | |||
if (IZ_JoystickInitializeConfig(state, config_path, argc, argv) < 0) { | |||
return -2; | |||
} | |||
u8 joysticks_count = SDL_NumJoysticks(); | |||
for (player_index = 0; player_index < joysticks_count; player_index += 1) { | |||
if (player_index >= IZ_PLAYERS) { | |||
break; | |||
} | |||
(*state)[player_index].device = SDL_JoystickOpen(state[player_index]->config.device_id); | |||
if (!(*state)[player_index].device) { | |||
break; | |||
} | |||
(*state)[player_index].config.device_id = SDL_JoystickInstanceID((*state)[player_index].device); | |||
SDL_GUID joystick_guid = SDL_JoystickGetGUID((*state)[player_index].device); | |||
IZ_memcpy(&(*state)[player_index].config.guid, sizeof(SDL_GUID), &joystick_guid, sizeof(SDL_GUID)); | |||
//(*state)[player_index].config.guid = joystick_guid; | |||
// printf("[INPUT:JOYSTICK] Initialize event from GUID: "); | |||
// for (u8 zz = 0; zz < 16; zz += 1) { | |||
// printf("%02x", (*state)[player_index].config.guid.data[zz]); | |||
// } | |||
// printf("\n"); | |||
} | |||
// Post config (after joystick GUIDs have been queried), this is unique to joysticks since they can be plugged in any | |||
// time. | |||
INI_ConfigSaveResult post_config_save_result = IZ_JoystickSaveConfig(state, config_path); | |||
if (post_config_save_result < 0) { | |||
return -3; | |||
} | |||
return 0; | |||
} | |||
void IZ_JoystickTeardown(IZ_JoystickState(* state)[IZ_PLAYERS]) { | |||
u8 player_index; | |||
for (player_index = 0; player_index < IZ_PLAYERS; player_index += 1) { | |||
if (!(*state)[player_index].device) { | |||
continue; | |||
} | |||
SDL_JoystickClose((*state)[player_index].device); | |||
} | |||
} |
@@ -1,106 +0,0 @@ | |||
#ifndef IZ_JOYSTICK_H | |||
#define IZ_JOYSTICK_H | |||
#include <SDL_joystick.h> | |||
#include <SDL_gamecontroller.h> | |||
#include <SDL_events.h> | |||
#include <ini-config.h> | |||
#include <ini-config/types/int.h> | |||
#include "../../config/IZ_config_guid.h" | |||
#include "../../common/IZ_common.h" | |||
#include "../../stdinc/IZ_string.h" | |||
#include "../../stdinc/IZ_stdlib.h" | |||
#include "IZ_action.h" | |||
typedef u8 IZ_PadButton; | |||
#define IZ_DEFAULT_AXIS_THRESHOLD ((u16) 8000) | |||
typedef enum { | |||
IZ_JOY_AXIS_DIRECTION_HORIZONTAL1 = 0, | |||
IZ_JOY_AXIS_DIRECTION_VERTICAL1 = 1, | |||
IZ_JOY_AXIS_DIRECTION_HORIZONTAL2 = 2, | |||
IZ_JOY_AXIS_DIRECTION_VERTICAL2 = 3, | |||
IZ_JOY_AXIS_DIRECTION_LEFT_SHOULDER = 4, | |||
IZ_JOY_AXIS_DIRECTION_RIGHT_SHOULDER = 5, | |||
} IZ_JoyAxisDirection; | |||
typedef struct { | |||
u16 axis_threshold; | |||
SDL_JoystickID device_id; | |||
SDL_GUID guid; | |||
IZ_PadButton control_mapping[IZ_CONTROLS]; | |||
} IZ_JoystickConfig; | |||
typedef struct { | |||
SDL_Joystick* device; | |||
IZ_JoystickConfig config; | |||
} IZ_JoystickState; | |||
static const IZ_JoystickState IZ_JOYSTICK_DEFAULT_STATE[IZ_PLAYERS] = { | |||
{ | |||
.config = { | |||
.control_mapping = { | |||
255, | |||
255, | |||
255, | |||
255, | |||
11, | |||
10, | |||
1, | |||
0, | |||
4, | |||
3, | |||
6, | |||
7, | |||
8, | |||
9, | |||
13, | |||
14, | |||
}, | |||
.axis_threshold = IZ_DEFAULT_AXIS_THRESHOLD, | |||
.device_id = 0, | |||
.guid = { | |||
.data = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } | |||
} | |||
}, | |||
.device = NULL, | |||
}, | |||
#if IZ_PLAYERS > 1 | |||
{ | |||
.config = { | |||
.control_mapping = { | |||
255, | |||
255, | |||
255, | |||
255, | |||
11, | |||
10, | |||
1, | |||
0, | |||
4, | |||
3, | |||
6, | |||
7, | |||
8, | |||
9, | |||
13, | |||
14, | |||
}, | |||
.axis_threshold = 8000u, | |||
.device_id = 1, | |||
}, | |||
.device = NULL, | |||
}, | |||
#endif | |||
}; | |||
IZ_ProcedureResult IZ_JoystickSaveConfig(IZ_JoystickState(*)[IZ_PLAYERS], const char*); | |||
void IZ_JoystickHandleEvents(IZ_JoystickState(*)[IZ_PLAYERS], IZ_Action(*)[IZ_PLAYERS], SDL_Event); | |||
IZ_ProcedureResult IZ_JoystickInitialize(IZ_JoystickState(*)[IZ_PLAYERS], const char*, u8, const char*[]); | |||
void IZ_JoystickTeardown(IZ_JoystickState(*)[IZ_PLAYERS]); | |||
#endif |
@@ -2,12 +2,12 @@ | |||
static INI_ConfigItem keyboard_config_items[IZ_PLAYERS * IZ_CONTROLS + 1]; | |||
void IZ_KeyboardSerializeControl(i32 value, char control[128]) { | |||
void IZ_KeyboardSerializeControl(i32 value, char control[128], void* _unused) { | |||
const char* serialized = SDL_GetKeyName(value); | |||
IZ_memcpy(control, 128, serialized, 128); | |||
} | |||
i32 IZ_KeyboardDeserializeControl(const char* control) { | |||
i32 IZ_KeyboardDeserializeControl(const char* control, void* _unused) { | |||
return SDL_GetKeyFromName(control); | |||
} | |||
@@ -4,7 +4,7 @@ | |||
#include <SDL_keyboard.h> | |||
#include <SDL_events.h> | |||
#include <ini-config.h> | |||
#include <ini-config/types/int.h> | |||
#include <ini-config/source/types/int.h> | |||
#include "../../stdinc/IZ_string.h" | |||
#include "../../stdinc/IZ_stdlib.h" | |||
#include "IZ_action.h" | |||
@@ -6,7 +6,7 @@ | |||
#include <midi-utils.h> | |||
#include <ini-config.h> | |||
#include <ini-config/types/int.h> | |||
#include <ini-config/source/types/int.h> | |||
#include "../../stdinc/IZ_string.h" | |||
#include "../../stdinc/IZ_stdlib.h" | |||
#include "IZ_action.h" | |||
@@ -3,8 +3,8 @@ | |||
#include <SDL_thread.h> | |||
#include <ini-config.h> | |||
#include <ini-config/types/int.h> | |||
#include <ini-config/types/string.h> | |||
#include <ini-config/source/types/int.h> | |||
#include <ini-config/source/types/string.h> | |||
#include "../common/IZ_common.h" | |||
#include "../game/input/IZ_action.h" | |||
@@ -1 +1 @@ | |||
Subproject commit b5007a86c4c9db88427362a9cda53ce8315cbfbd | |||
Subproject commit f3eb15d59ce81fb92a16375189f1d4755e2b7574 |