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_image/include" | ||||
"${CMAKE_HOME_DIRECTORY}/dependencies/SDL2_ttf/include" | "${CMAKE_HOME_DIRECTORY}/dependencies/SDL2_ttf/include" | ||||
"${CMAKE_HOME_DIRECTORY}/subprojects/ini-config/subprojects/minIni/dev" | "${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" | ||||
"${CMAKE_HOME_DIRECTORY}/subprojects/bdd-for-c-mocks" | "${CMAKE_HOME_DIRECTORY}/subprojects/bdd-for-c-mocks" | ||||
"${CMAKE_HOME_DIRECTORY}/subprojects/midi-utils" | "${CMAKE_HOME_DIRECTORY}/subprojects/midi-utils" | ||||
@@ -142,12 +142,12 @@ add_executable( | |||||
${IZ_EXECUTABLE_TYPE} | ${IZ_EXECUTABLE_TYPE} | ||||
subprojects/ini-config/subprojects/minIni/dev/minIni.h | subprojects/ini-config/subprojects/minIni/dev/minIni.h | ||||
subprojects/ini-config/subprojects/minIni/dev/minIni.c | 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.h | ||||
subprojects/midi-utils/midi-utils.c | subprojects/midi-utils/midi-utils.c | ||||
${IZ_GETOPT_DEPENDENCIES} | ${IZ_GETOPT_DEPENDENCIES} | ||||
@@ -158,8 +158,8 @@ add_executable( | |||||
src/packages/game/IZ_app.h | src/packages/game/IZ_app.h | ||||
src/packages/game/IZ_app.c | src/packages/game/IZ_app.c | ||||
src/packages/game/main.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.c | ||||
src/packages/game/input/IZ_keyboard.h | src/packages/game/input/IZ_keyboard.h | ||||
src/packages/game/geometry/IZ_vector2d.c | 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/bdd-for-c.h | ||||
subprojects/bdd-for-c-mocks/bdd-for-c-mocks.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_keyboard.mock.h | ||||
__mocks__/subprojects/SDL/SDL_events.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/SDL/SDL_stdinc.mock.h | ||||
__mocks__/subprojects/portmidi/portmidi.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.h | ||||
src/packages/config/IZ_config_guid.c | src/packages/config/IZ_config_guid.c | ||||
src/packages/game/input/IZ_keyboard.h | src/packages/game/input/IZ_keyboard.h | ||||
src/packages/game/input/IZ_keyboard.c | 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.h | ||||
src/packages/game/input/IZ_midi.c | src/packages/game/input/IZ_midi.c | ||||
@@ -350,12 +353,12 @@ add_executable( | |||||
dependencies/sqlite/sqlite3.c | dependencies/sqlite/sqlite3.c | ||||
subprojects/ini-config/subprojects/minIni/dev/minIni.h | subprojects/ini-config/subprojects/minIni/dev/minIni.h | ||||
subprojects/ini-config/subprojects/minIni/dev/minIni.c | 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.h | ||||
src/packages/log/IZ_intercept.c | src/packages/log/IZ_intercept.c | ||||
src/packages/server/main.c | src/packages/server/main.c | ||||
@@ -394,12 +397,12 @@ add_executable( | |||||
asset-inv | asset-inv | ||||
subprojects/ini-config/subprojects/minIni/dev/minIni.h | subprojects/ini-config/subprojects/minIni/dev/minIni.h | ||||
subprojects/ini-config/subprojects/minIni/dev/minIni.c | 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/common/IZ_common.h | ||||
src/packages/asset-inv/main.c | src/packages/asset-inv/main.c | ||||
) | ) | ||||
@@ -9,6 +9,10 @@ mock_modes(IZ_LogInfo) { | |||||
IZ_LOG_INFO_LOG, | 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(IZ_LogInfo) void IZ_LogInfo(IZ_LogCategory category, const char* context, const char* fmt, ...) { | ||||
mock_mode_if(IZ_LogInfo, IZ_LOG_INFO_SUPPRESS) { | mock_mode_if(IZ_LogInfo, IZ_LOG_INFO_SUPPRESS) { | ||||
mock_return(IZ_LogInfo); | 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_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_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_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_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_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_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_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_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_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_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_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_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_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_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; | mock_return(INI_ConfigSaveString) INI_CONFIG_SAVE_ITEM_OK; | ||||
} | } | ||||
@@ -3,29 +3,22 @@ | |||||
#endif | #endif | ||||
#include <bdd-for-c.h> | #include <bdd-for-c.h> | ||||
#include <subprojects/SDL/SDL_keyboard.mock.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/SDL/SDL_stdinc.mock.h> | ||||
#include <subprojects/minIni/minIni.mock.h> | #include <subprojects/minIni/minIni.mock.h> | ||||
#include <subprojects/portmidi/portmidi.mock.h> | #include <subprojects/portmidi/portmidi.mock.h> | ||||
#include <stdinc/IZ_string.mock.h> | #include <stdinc/IZ_string.mock.h> | ||||
#include <stdinc/IZ_stdlib.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_keyboard.h> | ||||
#include <game/input/IZ_joystick.h> | |||||
#include <game/input/IZ_gamecontroller.h> | |||||
#include <game/input/IZ_midi.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") { | spec("input") { | ||||
describe("joystick") { | |||||
describe("gamecontroller") { | |||||
describe("Initialize") { | describe("Initialize") { | ||||
static IZ_JoystickState state[IZ_PLAYERS]; | |||||
static IZ_GameControllerState state[IZ_PLAYERS]; | |||||
after_each() { | after_each() { | ||||
mock_reset(IZ_memcpy); | mock_reset(IZ_memcpy); | ||||
@@ -36,7 +29,7 @@ spec("input") { | |||||
} | } | ||||
after_each() { | after_each() { | ||||
mock_reset(SDL_JoystickOpen); | |||||
mock_reset(SDL_GameControllerOpen); | |||||
} | } | ||||
after_each() { | after_each() { | ||||
@@ -48,41 +41,41 @@ spec("input") { | |||||
} | } | ||||
it("sets initial state") { | 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(IZ_memcpy), "Initial state not loaded."); | ||||
check(mock_is_called(SDL_NumJoysticks), "Connected joysticks not checked."); | check(mock_is_called(SDL_NumJoysticks), "Connected joysticks not checked."); | ||||
} | } | ||||
it("calls load method") { | 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."); | check(mock_is_called(INI_ConfigInitialize), "Config load function not called."); | ||||
} | } | ||||
it("calls save method") { | 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."); | check(mock_is_called(INI_ConfigSave), "Config save function not called."); | ||||
} | } | ||||
it("opens device handles") { | 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( | 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") { | describe("HandleEvents") { | ||||
static SDL_Event e; | static SDL_Event e; | ||||
static IZ_JoystickState state[IZ_PLAYERS] = {}; | |||||
static IZ_GameControllerState state[IZ_PLAYERS] = {}; | |||||
static IZ_Action action[IZ_PLAYERS] = {}; | static IZ_Action action[IZ_PLAYERS] = {}; | ||||
u8 p; | u8 p; | ||||
@@ -90,267 +83,75 @@ spec("input") { | |||||
describe("on player %u", p) { | describe("on player %u", p) { | ||||
describe("on axis motion events") { | describe("on axis motion events") { | ||||
before_each() { | 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; | 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; | 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") { | 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; | 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") { | describe("SaveConfig") { | ||||
static IZ_JoystickState state[IZ_PLAYERS]; | |||||
static IZ_GameControllerState state[IZ_PLAYERS]; | |||||
after_each() { | after_each() { | ||||
mock_reset(INI_ConfigSave); | mock_reset(INI_ConfigSave); | ||||
@@ -368,13 +169,18 @@ spec("input") { | |||||
before_each() { | before_each() { | ||||
for (u8 p = 0; p < IZ_PLAYERS; p += 1) { | for (u8 p = 0; p < IZ_PLAYERS; p += 1) { | ||||
for (u8 i = 0; i < IZ_CONTROLS; i += 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") { | 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."); | check(mock_is_called(INI_ConfigSave), "Config save function not called."); | ||||
} | } | ||||
@@ -382,7 +188,7 @@ spec("input") { | |||||
describe("Teardown") { | describe("Teardown") { | ||||
static void* device = (void*) 1; | static void* device = (void*) 1; | ||||
static IZ_JoystickState state[IZ_PLAYERS] = {}; | |||||
static IZ_GameControllerState state[IZ_PLAYERS] = {}; | |||||
before_each() { | before_each() { | ||||
for (u8 p = 0; p < IZ_PLAYERS; p += 1) { | for (u8 p = 0; p < IZ_PLAYERS; p += 1) { | ||||
@@ -391,19 +197,19 @@ spec("input") { | |||||
} | } | ||||
after_each() { | after_each() { | ||||
mock_reset(SDL_JoystickClose); | |||||
mock_reset(SDL_GameControllerClose); | |||||
} | } | ||||
it("closes opened devices") { | 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( | 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 | # 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; | *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 raw_value; | ||||
static SDL_GUID default_value = { | static SDL_GUID default_value = { | ||||
.data = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, | .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_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); | SDL_GUID dest = *((SDL_GUID*) item->dest); | ||||
if (item->validator) { | if (item->validator) { | ||||
INI_ConfigLoadParamsGuidValidator* validate = 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" | #include "IZ_input.h" | ||||
void IZ_InputHandleSDLEvents(IZ_InputState* state, SDL_Event e) { | 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); | 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) { | *state = (IZ_InputState) { | ||||
.action = {}, | .action = {}, | ||||
.joystick_state = {}, | |||||
.game_controller_state = {}, | |||||
.midi_input_state = {}, | .midi_input_state = {}, | ||||
.keyboard_state = {}, | .keyboard_state = {}, | ||||
}; | }; | ||||
IZ_ProcedureResult result = 0; | 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; | result |= 1; | ||||
} | } | ||||
@@ -47,6 +47,6 @@ IZ_ProcedureResult IZ_InputInitialize(IZ_InputState* state, const char* config_p | |||||
void IZ_InputTeardown(IZ_InputState* state) { | void IZ_InputTeardown(IZ_InputState* state) { | ||||
IZ_LogInfo(IZ_LOG_CATEGORY_GLOBAL, "input", "Shutting down..."); | 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); | IZ_MIDIInputTeardown(&state->midi_input_state); | ||||
} | } |
@@ -3,14 +3,14 @@ | |||||
#include "IZ_action.h" | #include "IZ_action.h" | ||||
#include "IZ_keyboard.h" | #include "IZ_keyboard.h" | ||||
#include "IZ_joystick.h" | |||||
#include "IZ_gamecontroller.h" | |||||
#include "IZ_midi.h" | #include "IZ_midi.h" | ||||
#include "../../log/IZ_log.h" | #include "../../log/IZ_log.h" | ||||
typedef struct { | typedef struct { | ||||
IZ_Action action[IZ_PLAYERS]; | IZ_Action action[IZ_PLAYERS]; | ||||
IZ_KeyboardState keyboard_state[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_MIDIInputState midi_input_state[IZ_PLAYERS]; | ||||
} IZ_InputState; | } 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]; | 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); | const char* serialized = SDL_GetKeyName(value); | ||||
IZ_memcpy(control, 128, serialized, 128); | IZ_memcpy(control, 128, serialized, 128); | ||||
} | } | ||||
i32 IZ_KeyboardDeserializeControl(const char* control) { | |||||
i32 IZ_KeyboardDeserializeControl(const char* control, void* _unused) { | |||||
return SDL_GetKeyFromName(control); | return SDL_GetKeyFromName(control); | ||||
} | } | ||||
@@ -4,7 +4,7 @@ | |||||
#include <SDL_keyboard.h> | #include <SDL_keyboard.h> | ||||
#include <SDL_events.h> | #include <SDL_events.h> | ||||
#include <ini-config.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_string.h" | ||||
#include "../../stdinc/IZ_stdlib.h" | #include "../../stdinc/IZ_stdlib.h" | ||||
#include "IZ_action.h" | #include "IZ_action.h" | ||||
@@ -6,7 +6,7 @@ | |||||
#include <midi-utils.h> | #include <midi-utils.h> | ||||
#include <ini-config.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_string.h" | ||||
#include "../../stdinc/IZ_stdlib.h" | #include "../../stdinc/IZ_stdlib.h" | ||||
#include "IZ_action.h" | #include "IZ_action.h" | ||||
@@ -3,8 +3,8 @@ | |||||
#include <SDL_thread.h> | #include <SDL_thread.h> | ||||
#include <ini-config.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 "../common/IZ_common.h" | ||||
#include "../game/input/IZ_action.h" | #include "../game/input/IZ_action.h" | ||||
@@ -1 +1 @@ | |||||
Subproject commit b5007a86c4c9db88427362a9cda53ce8315cbfbd | |||||
Subproject commit f3eb15d59ce81fb92a16375189f1d4755e2b7574 |