Browse Source

Update gamepad event handling

Use game controller APIs instead of the joystick ones to be able to use
the gamepad button DB file.
master
TheoryOfNekomata 1 year ago
parent
commit
8bf3b46233
20 changed files with 612 additions and 823 deletions
  1. +29
    -26
      CMakeLists.txt
  2. +4
    -0
      __mocks__/src/packages/log/IZ_log.mock.h
  3. +48
    -0
      __mocks__/subprojects/SDL/SDL_gamecontroller.mock.h
  4. +0
    -43
      __mocks__/subprojects/SDL/SDL_joystick.mock.h
  5. +14
    -14
      __mocks__/subprojects/ini-config/source/ini-config.mock.h
  6. +83
    -277
      __tests__/src/packages/game/input.test.c
  7. BIN
      assets_src/gfx/weapons-rigged.cdr
  8. +43
    -18
      docs/controls.md
  9. +2
    -2
      src/packages/config/IZ_config_guid.c
  10. +272
    -0
      src/packages/game/input/IZ_gamecontroller.c
  11. +104
    -0
      src/packages/game/input/IZ_gamecontroller.h
  12. +4
    -4
      src/packages/game/input/IZ_input.c
  13. +2
    -2
      src/packages/game/input/IZ_input.h
  14. +0
    -324
      src/packages/game/input/IZ_joystick.c
  15. +0
    -106
      src/packages/game/input/IZ_joystick.h
  16. +2
    -2
      src/packages/game/input/IZ_keyboard.c
  17. +1
    -1
      src/packages/game/input/IZ_keyboard.h
  18. +1
    -1
      src/packages/game/input/IZ_midi.h
  19. +2
    -2
      src/packages/net/IZ_net_client.h
  20. +1
    -1
      subprojects/ini-config

+ 29
- 26
CMakeLists.txt View File

@@ -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
) )


+ 4
- 0
__mocks__/src/packages/log/IZ_log.mock.h View File

@@ -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);


+ 48
- 0
__mocks__/subprojects/SDL/SDL_gamecontroller.mock.h View File

@@ -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

+ 0
- 43
__mocks__/subprojects/SDL/SDL_joystick.mock.h View File

@@ -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

__mocks__/subprojects/ini-config/ini-config.mock.h → __mocks__/subprojects/ini-config/source/ini-config.mock.h View File

@@ -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;
} }



+ 83
- 277
__tests__/src/packages/game/input.test.c View File

@@ -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)
); );
} }
} }


BIN
assets_src/gfx/weapons-rigged.cdr View File


+ 43
- 18
docs/controls.md View File

@@ -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.

+ 2
- 2
src/packages/config/IZ_config_guid.c View File

@@ -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;


+ 272
- 0
src/packages/game/input/IZ_gamecontroller.c View File

@@ -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);
}
}

+ 104
- 0
src/packages/game/input/IZ_gamecontroller.h View File

@@ -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

+ 4
- 4
src/packages/game/input/IZ_input.c View File

@@ -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);
} }

+ 2
- 2
src/packages/game/input/IZ_input.h View File

@@ -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;




+ 0
- 324
src/packages/game/input/IZ_joystick.c View File

@@ -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);
}
}

+ 0
- 106
src/packages/game/input/IZ_joystick.h View File

@@ -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
- 2
src/packages/game/input/IZ_keyboard.c View File

@@ -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);
} }




+ 1
- 1
src/packages/game/input/IZ_keyboard.h View File

@@ -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"


+ 1
- 1
src/packages/game/input/IZ_midi.h View File

@@ -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"


+ 2
- 2
src/packages/net/IZ_net_client.h View File

@@ -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
subprojects/ini-config

@@ -1 +1 @@
Subproject commit b5007a86c4c9db88427362a9cda53ce8315cbfbd
Subproject commit f3eb15d59ce81fb92a16375189f1d4755e2b7574

Loading…
Cancel
Save