The game now uses MIDI support for alternative input methods.feature/data-structs
@@ -60,3 +60,4 @@ $RECYCLE.BIN/ | |||||
.idea/ | .idea/ | ||||
cmake-build-debug/ | cmake-build-debug/ | ||||
dependencies/ | dependencies/ | ||||
assets/ |
@@ -43,12 +43,13 @@ add_executable( | |||||
src/packages/game/input/IZ_keyboard.h | src/packages/game/input/IZ_keyboard.h | ||||
src/packages/game/IZ_config.c | src/packages/game/IZ_config.c | ||||
src/packages/game/IZ_config.h | src/packages/game/IZ_config.h | ||||
src/packages/game/geometry/IZ_point2d.c src/packages/game/geometry/IZ_point2d.h src/packages/game/geometry/IZ_vector2d.c src/packages/game/geometry/IZ_vector2d.h src/packages/game/geometry/IZ_rect.c src/packages/game/geometry/IZ_rect.h src/packages/game/core/IZ_object.c src/packages/game/core/IZ_object.h src/packages/game/core/IZ_creature.c src/packages/game/core/IZ_creature.h src/packages/game/core/IZ_entity.c src/packages/game/core/IZ_entity.h src/packages/game/memory/IZ_pool.c src/packages/game/memory/IZ_pool.h src/packages/game/input/IZ_input.c src/packages/game/input/IZ_input.h) | |||||
src/packages/game/geometry/IZ_point2d.c src/packages/game/geometry/IZ_point2d.h src/packages/game/geometry/IZ_vector2d.c src/packages/game/geometry/IZ_vector2d.h src/packages/game/geometry/IZ_rect.c src/packages/game/geometry/IZ_rect.h src/packages/game/core/IZ_object.c src/packages/game/core/IZ_object.h src/packages/game/core/IZ_creature.c src/packages/game/core/IZ_creature.h src/packages/game/core/IZ_entity.c src/packages/game/core/IZ_entity.h src/packages/game/memory/IZ_pool.c src/packages/game/memory/IZ_pool.h src/packages/game/input/IZ_input.c src/packages/game/input/IZ_input.h src/packages/game/input/IZ_midi.c src/packages/game/input/IZ_midi.h) | |||||
target_link_libraries( | target_link_libraries( | ||||
game | game | ||||
SDL2main | SDL2main | ||||
SDL2 | SDL2 | ||||
portmidi | |||||
) | ) | ||||
add_executable( | add_executable( | ||||
@@ -109,3 +110,10 @@ if (WIN32) | |||||
"${PROJECT_SOURCE_DIR}/dependencies/SDL2/lib/${PROJECT_ARCH}/SDL2.dll" # <--this is in-file | "${PROJECT_SOURCE_DIR}/dependencies/SDL2/lib/${PROJECT_ARCH}/SDL2.dll" # <--this is in-file | ||||
$<TARGET_FILE_DIR:game>) # <--this is out-file path | $<TARGET_FILE_DIR:game>) # <--this is out-file path | ||||
endif () | endif () | ||||
if (WIN32) | |||||
add_custom_command(TARGET game POST_BUILD | |||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different # which executes "cmake - E copy_if_different..." | |||||
"${PROJECT_SOURCE_DIR}/dependencies/portmidi/Release/portmidi.dll" # <--this is in-file | |||||
$<TARGET_FILE_DIR:game>) # <--this is out-file path | |||||
endif () |
@@ -17,14 +17,79 @@ IZ_ProcedureResult IZ_InitializeApp(IZ_App* app) { | |||||
return 2; | return 2; | ||||
} | } | ||||
for (uint8_t p = 0; p < PLAYERS; p += 1) { | |||||
IZ_InitializeInput(config_path, &app->input_state[p], p); | |||||
} | |||||
IZ_InitializeInput(config_path, &app->input_state); | |||||
app->quit = false; | |||||
return 0; | return 0; | ||||
} | } | ||||
void IZ_TeardownApp(IZ_App* app) { | void IZ_TeardownApp(IZ_App* app) { | ||||
IZ_TeardownInput(&app->input_state); | |||||
IZ_TeardownVideo(&app->video_state); | IZ_TeardownVideo(&app->video_state); | ||||
SDL_Quit(); | SDL_Quit(); | ||||
} | } | ||||
void IZ_HandleSDLEvents(IZ_App* app) { | |||||
while (SDL_PollEvent(&app->input_state.sdl_event) != 0) { | |||||
if (app->input_state.sdl_event.type == SDL_QUIT) { | |||||
app->quit = true; | |||||
break; | |||||
} | |||||
IZ_HandleSDLInputEvents(app->input_state.sdl_event, &app->input_state); | |||||
} | |||||
} | |||||
void IZ_HandlePortMIDIEvents(IZ_App* app) { | |||||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||||
int32_t midi_events_count = Pm_Read( | |||||
app->input_state.midi_input_state[player_index].stream, | |||||
app->input_state.midi_input_state[player_index].event_buffer, | |||||
1024 | |||||
); | |||||
if (midi_events_count > 0) { | |||||
for (int32_t midi_event_index = 0; midi_event_index < midi_events_count; midi_event_index += 1) { | |||||
IZ_HandlePortMIDIInputEvents( | |||||
app->input_state.midi_input_state->event_buffer[midi_event_index], | |||||
&app->input_state | |||||
); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
void IZ_HandleEvents(IZ_App* app) { | |||||
IZ_HandleSDLEvents(app); | |||||
IZ_HandlePortMIDIEvents(app); | |||||
} | |||||
IZ_ProcedureResult IZ_RunApp(IZ_App* app, uint8_t argc, char* argv[]) { | |||||
printf_s("Args (%u):\n", argc); | |||||
for (uint8_t i = 0; i < argc; i += 1) { | |||||
printf_s(" %s", argv[i]); | |||||
} | |||||
IZ_ProcedureResult init_result = IZ_InitializeApp(app); | |||||
if (init_result) { | |||||
return init_result; | |||||
} | |||||
while (true) { | |||||
uint64_t ticks = SDL_GetTicks64(); | |||||
// TODO do audio processing | |||||
// TODO do networking? | |||||
// process events | |||||
IZ_HandleEvents(app); | |||||
if (app->quit) { | |||||
break; | |||||
} | |||||
IZ_UpdateVideo(&app->video_state, &app->input_state, ticks); | |||||
} | |||||
IZ_TeardownApp(app); | |||||
return 0; | |||||
} |
@@ -2,20 +2,19 @@ | |||||
#define IZ_APP_H | #define IZ_APP_H | ||||
#include <SDL.h> | #include <SDL.h> | ||||
#include <stdbool.h> | |||||
#include "input/IZ_input.h" | #include "input/IZ_input.h" | ||||
#include "output/IZ_video.h" | #include "output/IZ_video.h" | ||||
#include "memory/IZ_pool.h" | #include "memory/IZ_pool.h" | ||||
#include "input/IZ_action.h" | |||||
typedef struct { | typedef struct { | ||||
IZ_InputState input_state[PLAYERS]; | |||||
IZ_InputState input_state; | |||||
IZ_VideoState video_state; | IZ_VideoState video_state; | ||||
IZ_Pool memory_pool; | IZ_Pool memory_pool; | ||||
bool quit; | |||||
} IZ_App; | } IZ_App; | ||||
IZ_ProcedureResult IZ_InitializeApp(IZ_App*); | |||||
void IZ_TeardownApp(IZ_App*); | |||||
IZ_ProcedureResult IZ_RunApp(IZ_App*, uint8_t, char**); | |||||
#endif | #endif |
@@ -1,18 +1,38 @@ | |||||
#include "IZ_input.h" | #include "IZ_input.h" | ||||
void IZ_HandleInputEvents(SDL_Event e, IZ_InputState* state) { | |||||
void IZ_HandleSDLInputEvents(SDL_Event e, IZ_InputState* state) { | |||||
IZ_HandleJoystickEvents(e, &state->joystick_state, &state->action); | IZ_HandleJoystickEvents(e, &state->joystick_state, &state->action); | ||||
IZ_HandleKeyboardEvents(e, &state->keyboard_state, &state->action); | IZ_HandleKeyboardEvents(e, &state->keyboard_state, &state->action); | ||||
} | } | ||||
void IZ_InitializeInput(const char* config_path, IZ_InputState* state, uint8_t p) { | |||||
if (IZ_InitializeKeyboardConfig(config_path, &state->keyboard_state.config, p)) { | |||||
fprintf_s(stderr, "Error committing keyboard config for player %d.\n", p); | |||||
void IZ_HandlePortMIDIInputEvents(PmEvent e, IZ_InputState* state) { | |||||
IZ_HandleMIDIInputEvents(e, &state->midi_input_state, &state->action); | |||||
} | |||||
void IZ_InitializeInput(const char* config_path, IZ_InputState* state) { | |||||
*state = (IZ_InputState) {}; | |||||
IZ_ProcedureResult joystick_result = IZ_InitializeJoystickState(config_path, &state->joystick_state); | |||||
if (joystick_result) { | |||||
fprintf_s(stderr, "Error committing joystick config. Code: %u.\n", joystick_result); | |||||
} | |||||
IZ_ProcedureResult keyboard_result = IZ_InitializeKeyboardState(config_path, &state->keyboard_state); | |||||
if (keyboard_result) { | |||||
fprintf_s(stderr, "Error committing keyboard config. Code: %u.\n", keyboard_result); | |||||
} | |||||
IZ_ProcedureResult midi_input_result = IZ_InitializeMIDIInput(config_path, &state->midi_input_state); | |||||
if (midi_input_result) { | |||||
fprintf_s(stderr, "Error committing MIDI input config. Code: %u.\n", midi_input_result); | |||||
} | } | ||||
if (IZ_InitializeJoystickConfig(config_path, &state->joystick_state.config, p)) { | |||||
fprintf_s(stderr, "Error committing joystick config for player %d.\n", p); | |||||
for (uint8_t p = 0; p < PLAYERS; p += 1) { | |||||
state->action[p] = 0; | |||||
} | } | ||||
} | |||||
state->action = 0; | |||||
void IZ_TeardownInput(IZ_InputState* state) { | |||||
IZ_TeardownJoystickState(&state->joystick_state); | |||||
IZ_TeardownMIDIInput(&state->midi_input_state); | |||||
} | } |
@@ -4,15 +4,22 @@ | |||||
#include "IZ_action.h" | #include "IZ_action.h" | ||||
#include "IZ_keyboard.h" | #include "IZ_keyboard.h" | ||||
#include "IZ_joystick.h" | #include "IZ_joystick.h" | ||||
#include "IZ_midi.h" | |||||
typedef struct { | typedef struct { | ||||
IZ_Action action; | |||||
IZ_KeyboardState keyboard_state; | |||||
IZ_JoystickState joystick_state; | |||||
IZ_Action action[PLAYERS]; | |||||
SDL_Event sdl_event; | |||||
IZ_KeyboardState keyboard_state[PLAYERS]; | |||||
IZ_JoystickState joystick_state[PLAYERS]; | |||||
IZ_MIDIInputState midi_input_state[PLAYERS]; | |||||
} IZ_InputState; | } IZ_InputState; | ||||
void IZ_HandleInputEvents(SDL_Event, IZ_InputState*); | |||||
void IZ_HandleSDLInputEvents(SDL_Event, IZ_InputState*); | |||||
void IZ_InitializeInput(const char*, IZ_InputState*, uint8_t); | |||||
void IZ_HandlePortMIDIInputEvents(PmEvent, IZ_InputState*); | |||||
void IZ_InitializeInput(const char*, IZ_InputState*); | |||||
void IZ_TeardownInput(IZ_InputState*); | |||||
#endif | #endif |
@@ -2,18 +2,18 @@ | |||||
void IZ_HandleJoystickDeviceEvents(SDL_Event e, IZ_JoystickState* state) { | void IZ_HandleJoystickDeviceEvents(SDL_Event e, IZ_JoystickState* state) { | ||||
if (e.type == SDL_JOYDEVICEADDED) { | if (e.type == SDL_JOYDEVICEADDED) { | ||||
if (SDL_NumJoysticks() <= PLAYERS && !state->joystick_instance) { | |||||
state->joystick_instance = SDL_JoystickOpen(e.jdevice.which); | |||||
if (SDL_NumJoysticks() <= PLAYERS && !state->device) { | |||||
state->device = SDL_JoystickOpen(e.jdevice.which); | |||||
} | } | ||||
return; | return; | ||||
} | } | ||||
if (e.type == SDL_JOYDEVICEREMOVED) { | if (e.type == SDL_JOYDEVICEREMOVED) { | ||||
if ( | if ( | ||||
state->joystick_instance | |||||
&& SDL_JoystickInstanceID(state->joystick_instance) == e.jdevice.which | |||||
state->device | |||||
&& SDL_JoystickInstanceID(state->device) == e.jdevice.which | |||||
) { | ) { | ||||
state->joystick_instance = NULL; | |||||
state->device = NULL; | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -82,56 +82,97 @@ void IZ_HandleJoystickButtonEvents(SDL_Event e, IZ_JoystickState* state, IZ_Acti | |||||
} | } | ||||
} | } | ||||
void IZ_HandleJoystickEvents(SDL_Event e, IZ_JoystickState* state, IZ_Action* action) { | |||||
IZ_HandleJoystickDeviceEvents(e, state); | |||||
IZ_HandleJoystickAxisEvents(e, state, action); | |||||
IZ_HandleJoystickHatEvents(e, action); | |||||
IZ_HandleJoystickButtonEvents(e, state, action); | |||||
void IZ_HandleJoystickEvents(SDL_Event e, IZ_JoystickState(* state)[PLAYERS], IZ_Action(* action)[PLAYERS]) { | |||||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||||
IZ_HandleJoystickDeviceEvents(e, state[player_index]); | |||||
IZ_HandleJoystickAxisEvents(e, state[player_index], action[player_index]); | |||||
IZ_HandleJoystickHatEvents(e, action[player_index]); | |||||
IZ_HandleJoystickButtonEvents(e, state[player_index], action[player_index]); | |||||
} | |||||
} | } | ||||
void IZ_LoadJoystickConfig(const char* config_path, IZ_JoystickConfig* config, uint8_t player_index) { | |||||
char joystick_control_mapping_section_name[26]; | |||||
sprintf_s(joystick_control_mapping_section_name, 26, "Joystick.%d.ControlMapping", player_index); | |||||
void IZ_LoadJoystickConfig(const char* config_path, IZ_JoystickState(* state)[PLAYERS]) { | |||||
char control_mapping_section_name[26]; | |||||
char main_section_name[11]; | |||||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||||
sprintf_s(control_mapping_section_name, 26, "Joystick.%d.ControlMapping", player_index); | |||||
for (uint8_t i = 4; i < CONTROLS; i += 1) { | |||||
state[player_index]->config.control_mapping[i] = ini_getl( | |||||
control_mapping_section_name, | |||||
ACTION_NAMES[i], | |||||
IZ_DEFAULT_JOYSTICK_CONTROLS[player_index][i], | |||||
config_path | |||||
); | |||||
} | |||||
for (uint8_t i = 4; i < CONTROLS; i += 1) { | |||||
config->control_mapping[i] = ini_getl(joystick_control_mapping_section_name, ACTION_NAMES[i], IZ_DEFAULT_JOYSTICK_CONTROLS[player_index][i], config_path); | |||||
sprintf_s(main_section_name, 11, "Joystick.%d", player_index); | |||||
state[player_index]->config.axis_threshold = ini_getl(main_section_name, "AxisThreshold", IZ_DEFAULT_AXIS_THRESHOLD, config_path); | |||||
state[player_index]->config.device_id = ini_getl(main_section_name, "DeviceID", player_index, config_path); | |||||
} | } | ||||
char joystick_section_name[] = "Joystick.0"; | |||||
joystick_section_name[9] = (char) (48 + player_index); | |||||
config->axis_threshold = ini_getl(joystick_section_name, "AxisThreshold", 8000, config_path); | |||||
} | } | ||||
IZ_ProcedureResult IZ_SaveJoystickConfig(const char* config_path, IZ_JoystickConfig* config, uint8_t player_index) { | |||||
char joystick_control_mapping_section_name[26]; | |||||
sprintf_s(joystick_control_mapping_section_name, 26, "Joystick.%d.ControlMapping", player_index); | |||||
IZ_ProcedureResult IZ_SaveJoystickConfig(const char* config_path, IZ_JoystickState(* state)[PLAYERS]) { | |||||
uint8_t problem = 0; | |||||
char control_mapping_section_name[26]; | |||||
char main_section_name[11]; | |||||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||||
sprintf_s(control_mapping_section_name, 26, "Joystick.%d.ControlMapping", player_index); | |||||
for (uint8_t i = 4; i < CONTROLS; i += 1) { | |||||
if (!ini_putl( | |||||
control_mapping_section_name, | |||||
ACTION_NAMES[i], | |||||
state[player_index]->config.control_mapping[i], | |||||
config_path | |||||
)) { | |||||
return 1; | |||||
} | |||||
} | |||||
for (uint8_t i = 4; i < CONTROLS; i += 1) { | |||||
sprintf_s(main_section_name, 11, "Joystick.%d", player_index); | |||||
if (!ini_putl( | if (!ini_putl( | ||||
joystick_control_mapping_section_name, | |||||
ACTION_NAMES[i], | |||||
config->control_mapping[i], | |||||
config_path) | |||||
) { | |||||
return 1; | |||||
main_section_name, | |||||
"DeviceID", | |||||
state[player_index]->config.device_id, | |||||
config_path | |||||
)) { | |||||
problem |= (1 << player_index); | |||||
} | |||||
if (!ini_putl( | |||||
main_section_name, | |||||
"AxisThreshold", | |||||
state[player_index]->config.axis_threshold, | |||||
config_path | |||||
)) { | |||||
problem |= (1 << player_index); | |||||
} | } | ||||
} | } | ||||
char joystick_section_name[] = "Joystick.0"; | |||||
joystick_section_name[9] = (char) (48 + player_index); | |||||
if (!ini_putl( | |||||
joystick_section_name, | |||||
"AxisThreshold", | |||||
config->axis_threshold, | |||||
config_path) | |||||
) { | |||||
return problem; | |||||
} | |||||
IZ_ProcedureResult IZ_InitializeJoystickState(const char* config_path, IZ_JoystickState(* state)[PLAYERS]) { | |||||
IZ_LoadJoystickConfig(config_path, state); | |||||
if (IZ_SaveJoystickConfig(config_path, state)) { | |||||
return 1; | return 1; | ||||
} | } | ||||
uint8_t joysticks_count = SDL_NumJoysticks(); | |||||
for (uint8_t player_index = 0; player_index < joysticks_count; player_index += 1) { | |||||
state[player_index]->device = SDL_JoystickOpen(state[player_index]->config.device_id); | |||||
} | |||||
return 0; | return 0; | ||||
} | } | ||||
IZ_ProcedureResult IZ_InitializeJoystickConfig(const char* config_path, IZ_JoystickConfig* config, uint8_t p) { | |||||
IZ_LoadJoystickConfig(config_path, config, p); | |||||
return IZ_SaveJoystickConfig(config_path, config, p); | |||||
void IZ_TeardownJoystickState(IZ_JoystickState(* state)[PLAYERS]) { | |||||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||||
if (!state[player_index]->device) { | |||||
continue; | |||||
} | |||||
SDL_JoystickClose(state[player_index]->device); | |||||
} | |||||
} | } |
@@ -8,6 +8,8 @@ | |||||
typedef uint8_t IZ_PadButton; | typedef uint8_t IZ_PadButton; | ||||
static const uint16_t IZ_DEFAULT_AXIS_THRESHOLD = 8000; | |||||
typedef enum { | typedef enum { | ||||
IZ_JOY_AXIS_DIRECTION_HORIZONTAL1 = 0, | IZ_JOY_AXIS_DIRECTION_HORIZONTAL1 = 0, | ||||
IZ_JOY_AXIS_DIRECTION_VERTICAL1 = 1, | IZ_JOY_AXIS_DIRECTION_VERTICAL1 = 1, | ||||
@@ -17,11 +19,12 @@ typedef enum { | |||||
typedef struct { | typedef struct { | ||||
uint16_t axis_threshold; | uint16_t axis_threshold; | ||||
SDL_JoystickID device_id; | |||||
IZ_PadButton control_mapping[CONTROLS]; | IZ_PadButton control_mapping[CONTROLS]; | ||||
} IZ_JoystickConfig; | } IZ_JoystickConfig; | ||||
typedef struct { | typedef struct { | ||||
SDL_Joystick* joystick_instance; | |||||
SDL_Joystick* device; | |||||
IZ_JoystickConfig config; | IZ_JoystickConfig config; | ||||
} IZ_JoystickState; | } IZ_JoystickState; | ||||
@@ -46,10 +49,12 @@ static IZ_PadButton IZ_DEFAULT_JOYSTICK_CONTROLS[PLAYERS][CONTROLS] = { | |||||
}, | }, | ||||
}; | }; | ||||
IZ_ProcedureResult IZ_SaveJoystickConfig(const char*, IZ_JoystickConfig*, uint8_t); | |||||
IZ_ProcedureResult IZ_SaveJoystickConfig(const char*, IZ_JoystickState(*)[PLAYERS]); | |||||
void IZ_HandleJoystickEvents(SDL_Event, IZ_JoystickState(*)[PLAYERS], IZ_Action(*)[PLAYERS]); | |||||
void IZ_HandleJoystickEvents(SDL_Event, IZ_JoystickState*, IZ_Action*); | |||||
IZ_ProcedureResult IZ_InitializeJoystickState(const char*, IZ_JoystickState(*)[PLAYERS]); | |||||
IZ_ProcedureResult IZ_InitializeJoystickConfig(const char*, IZ_JoystickConfig*, uint8_t); | |||||
void IZ_TeardownJoystickState(IZ_JoystickState(*)[PLAYERS]); | |||||
#endif | #endif |
@@ -1,6 +1,6 @@ | |||||
#include "IZ_keyboard.h" | #include "IZ_keyboard.h" | ||||
void IZ_HandleKeyboardEvents(SDL_Event e, IZ_KeyboardState* state, IZ_Action* action) { | |||||
void IZ_HandleKewyboardKeyUpDownEvents(SDL_Event e, IZ_KeyboardState* state, IZ_Action* action) { | |||||
for (uint8_t i = 0; i < CONTROLS; i += 1) { | for (uint8_t i = 0; i < CONTROLS; i += 1) { | ||||
if (e.key.keysym.sym == state->config.control_mapping[i]) { | if (e.key.keysym.sym == state->config.control_mapping[i]) { | ||||
const uint16_t bitflag = (0x1 << i); | const uint16_t bitflag = (0x1 << i); | ||||
@@ -16,42 +16,53 @@ void IZ_HandleKeyboardEvents(SDL_Event e, IZ_KeyboardState* state, IZ_Action* ac | |||||
} | } | ||||
} | } | ||||
IZ_ProcedureResult IZ_SaveKeyboardConfig(const char* config_path, IZ_KeyboardConfig* config, uint8_t player_index) { | |||||
char keyboard_section_name[26]; | |||||
sprintf_s(keyboard_section_name, 26, "Keyboard.%d.ControlMapping", player_index); | |||||
for (uint8_t i = 0; i < CONTROLS; i += 1) { | |||||
if (!ini_puts( | |||||
keyboard_section_name, | |||||
ACTION_NAMES[i], | |||||
SDL_GetKeyName(config->control_mapping[i]), | |||||
config_path) | |||||
) { | |||||
return 1; | |||||
void IZ_HandleKeyboardEvents(SDL_Event e, IZ_KeyboardState(* state)[PLAYERS], IZ_Action(* action)[PLAYERS]) { | |||||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||||
IZ_HandleKewyboardKeyUpDownEvents(e, state[player_index], action[player_index]); | |||||
} | |||||
} | |||||
IZ_ProcedureResult IZ_SaveKeyboardConfig(const char* config_path, IZ_KeyboardState(* state)[PLAYERS]) { | |||||
uint8_t problem = 0; | |||||
char control_mapping_section_name[26]; | |||||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||||
sprintf_s(control_mapping_section_name, 26, "Keyboard.%d.ControlMapping", player_index); | |||||
for (uint8_t i = 0; i < CONTROLS; i += 1) { | |||||
if (!ini_puts( | |||||
control_mapping_section_name, | |||||
ACTION_NAMES[i], | |||||
SDL_GetKeyName(state[player_index]->config.control_mapping[i]), | |||||
config_path | |||||
)) { | |||||
problem |= (1 << player_index); | |||||
} | |||||
} | } | ||||
} | } | ||||
return 0; | |||||
return problem; | |||||
} | } | ||||
void IZ_LoadKeyboardConfig(const char* config_path, IZ_KeyboardConfig* config, uint8_t player_index) { | |||||
void IZ_LoadKeyboardConfig(const char* config_path, IZ_KeyboardState(* state)[PLAYERS]) { | |||||
char buffer[128]; | char buffer[128]; | ||||
char keyboard_section_name[26]; | char keyboard_section_name[26]; | ||||
sprintf_s(keyboard_section_name, 26, "Keyboard.%d.ControlMapping", player_index); | |||||
for (uint8_t i = 0; i < CONTROLS; i += 1) { | |||||
ini_gets( | |||||
keyboard_section_name, | |||||
ACTION_NAMES[i], | |||||
SDL_GetKeyName(IZ_DEFAULT_KEYBOARD_CONTROLS[player_index][i]), | |||||
buffer, | |||||
128, | |||||
config_path | |||||
); | |||||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||||
sprintf_s(keyboard_section_name, 26, "Keyboard.%d.ControlMapping", player_index); | |||||
for (uint8_t i = 0; i < CONTROLS; i += 1) { | |||||
ini_gets( | |||||
keyboard_section_name, | |||||
ACTION_NAMES[i], | |||||
SDL_GetKeyName(IZ_DEFAULT_KEYBOARD_CONTROLS[player_index][i]), | |||||
buffer, | |||||
128, | |||||
config_path | |||||
); | |||||
config->control_mapping[i] = SDL_GetKeyFromName(buffer); | |||||
state[player_index]->config.control_mapping[i] = SDL_GetKeyFromName(buffer); | |||||
} | |||||
} | } | ||||
} | } | ||||
IZ_ProcedureResult IZ_InitializeKeyboardConfig(const char* config_path, IZ_KeyboardConfig* config, uint8_t p) { | |||||
IZ_LoadKeyboardConfig(config_path, config, p); | |||||
return IZ_SaveKeyboardConfig(config_path, config, p); | |||||
IZ_ProcedureResult IZ_InitializeKeyboardState(const char* config_path, IZ_KeyboardState(* state)[PLAYERS]) { | |||||
IZ_LoadKeyboardConfig(config_path, state); | |||||
return IZ_SaveKeyboardConfig(config_path, state); | |||||
} | } |
@@ -35,10 +35,10 @@ static const SDL_KeyCode IZ_DEFAULT_KEYBOARD_CONTROLS[PLAYERS][CONTROLS] = { | |||||
}, | }, | ||||
}; | }; | ||||
IZ_ProcedureResult IZ_SaveKeyboardConfig(const char*, IZ_KeyboardConfig*, uint8_t); | |||||
IZ_ProcedureResult IZ_SaveKeyboardConfig(const char*, IZ_KeyboardState(*)[PLAYERS]); | |||||
void IZ_HandleKeyboardEvents(SDL_Event, IZ_KeyboardState*, IZ_Action*); | |||||
void IZ_HandleKeyboardEvents(SDL_Event, IZ_KeyboardState(*)[PLAYERS], IZ_Action(*)[PLAYERS]); | |||||
IZ_ProcedureResult IZ_InitializeKeyboardConfig(const char*, IZ_KeyboardConfig*, uint8_t); | |||||
IZ_ProcedureResult IZ_InitializeKeyboardState(const char*, IZ_KeyboardState(*)[PLAYERS]); | |||||
#endif | #endif |
@@ -0,0 +1,181 @@ | |||||
#include "IZ_midi.h" | |||||
char* IZ_GetMIDINoteName(uint8_t midi_note) { | |||||
static const char* pitch_names[] = { | |||||
"C", | |||||
"C#", | |||||
"D", | |||||
"D#", | |||||
"E", | |||||
"F", | |||||
"F#", | |||||
"G", | |||||
"G#", | |||||
"A", | |||||
"A#", | |||||
"B" | |||||
}; | |||||
const uint8_t pitch_class = midi_note % 12; | |||||
const uint8_t octave = midi_note / 12; | |||||
static char note_name[4]; | |||||
sprintf_s(note_name, 4, "%s%u", pitch_names[pitch_class], octave); | |||||
return note_name; | |||||
} | |||||
uint8_t IZ_GetMIDINoteFromName(char* name) { | |||||
char name_copy[4]; | |||||
memcpy_s(name_copy, 4, name, 4); | |||||
_strlwr_s(name_copy, 4); | |||||
uint8_t octave; | |||||
const char base_pitch_name[] = "c d ef g a b"; | |||||
if (strlen(name_copy) == 2) { | |||||
octave = name_copy[1] - '0'; | |||||
for (uint8_t i = 0; i < 12; i += 1) { | |||||
if (base_pitch_name[i] == name_copy[0]) { | |||||
return (octave * 12) + i; | |||||
} | |||||
} | |||||
return 255u; | |||||
} | |||||
uint8_t pitch_class; | |||||
octave = name_copy[2] - '0'; | |||||
if (strstr(name_copy, "c#") || strstr(name_copy, "db")) { | |||||
pitch_class = 1; | |||||
} else if (strstr(name_copy, "d#") || strstr(name_copy, "eb")) { | |||||
pitch_class = 3; | |||||
} else if (strstr(name_copy, "f#") || strstr(name_copy, "gb")) { | |||||
pitch_class = 6; | |||||
} else if (strstr(name_copy, "g#") || strstr(name_copy, "ab")) { | |||||
pitch_class = 8; | |||||
} else if (strstr(name_copy, "a#") || strstr(name_copy, "bb")) { | |||||
pitch_class = 10; | |||||
} else { | |||||
return 255u; | |||||
} | |||||
return (octave * 12) + pitch_class; | |||||
} | |||||
void IZ_HandleMIDINoteOnOffEvents(PmEvent e, IZ_MIDIInputState* state, IZ_Action* action) { | |||||
uint32_t message = e.message; | |||||
uint8_t status = message & 0xFFu; | |||||
uint8_t data1 = (message >> 8) & 0xFFu; | |||||
// uint8_t data2 = (message >> 16) & 0xFFu; | |||||
for (uint8_t i = 0; i < CONTROLS; i += 1) { | |||||
if (data1 == state->config.control_mapping[i]) { | |||||
const uint16_t bitflag = (0x1 << i); | |||||
if (status == IZ_MIDI_NOTE_ON) { | |||||
*action |= bitflag; | |||||
return; | |||||
} | |||||
if (status == IZ_MIDI_NOTE_OFF) { | |||||
*action &= ~bitflag; | |||||
return; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
void IZ_HandleMIDIInputEvents(PmEvent e, IZ_MIDIInputState(* state)[PLAYERS], IZ_Action(* action)[PLAYERS]) { | |||||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||||
IZ_HandleMIDINoteOnOffEvents(e, state[player_index], action[player_index]); | |||||
} | |||||
} | |||||
IZ_ProcedureResult IZ_SaveMIDIInputConfig(const char* config_path, IZ_MIDIInputState(* state)[PLAYERS]) { | |||||
uint8_t problem = 0; | |||||
char control_mapping_section_name[27]; | |||||
char main_section_name[12]; | |||||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||||
sprintf_s(control_mapping_section_name, 27, "MIDIInput.%d.ControlMapping", player_index); | |||||
for (uint8_t i = 0; i < CONTROLS; i += 1) { | |||||
if (!ini_puts( | |||||
control_mapping_section_name, | |||||
ACTION_NAMES[i], | |||||
IZ_GetMIDINoteName(state[player_index]->config.control_mapping[i]), | |||||
config_path | |||||
)) { | |||||
problem |= (1 << player_index); | |||||
} | |||||
} | |||||
sprintf_s(main_section_name, 12, "MIDIInput.%d", player_index); | |||||
if (!ini_putl( | |||||
main_section_name, | |||||
"DeviceID", | |||||
state[player_index]->config.device_id, | |||||
config_path | |||||
)) { | |||||
problem |= (1 << player_index); | |||||
} | |||||
} | |||||
return problem; | |||||
} | |||||
void IZ_LoadMIDIInputConfig(const char* config_path, IZ_MIDIInputState(* state)[PLAYERS]) { | |||||
char buffer[128]; | |||||
char control_mapping_section_name[27]; | |||||
char main_section_name[12]; | |||||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||||
sprintf_s(control_mapping_section_name, 27, "MIDIInput.%d.ControlMapping", player_index); | |||||
for (uint8_t i = 0; i < CONTROLS; i += 1) { | |||||
ini_gets( | |||||
control_mapping_section_name, | |||||
ACTION_NAMES[i], | |||||
IZ_GetMIDINoteName(IZ_DEFAULT_MIDI_INPUT_CONTROLS[player_index][i]), | |||||
buffer, | |||||
128, | |||||
config_path | |||||
); | |||||
state[player_index]->config.control_mapping[i] = IZ_GetMIDINoteFromName(buffer); | |||||
} | |||||
sprintf_s(main_section_name, 12, "MIDIInput.%d", player_index); | |||||
state[player_index]->config.device_id = ini_getl(main_section_name, "DeviceID", player_index, config_path); | |||||
} | |||||
} | |||||
IZ_ProcedureResult IZ_InitializeMIDIInput(const char* config_path, IZ_MIDIInputState(* state)[PLAYERS]) { | |||||
if (Pm_Initialize()) { | |||||
return 1; | |||||
} | |||||
IZ_LoadMIDIInputConfig(config_path, state); | |||||
if (IZ_SaveMIDIInputConfig(config_path, state)) { | |||||
return 2; | |||||
} | |||||
for (uint8_t player_index = 0; player_index < PLAYERS; player_index += 1) { | |||||
state[player_index]->device_info = Pm_GetDeviceInfo(state[player_index]->config.device_id); | |||||
state[player_index]->stream = NULL; | |||||
Pm_OpenInput( | |||||
&state[player_index]->stream, | |||||
state[player_index]->config.device_id, | |||||
NULL, | |||||
MIDI_EVENT_BUFFER_SIZE, | |||||
NULL, | |||||
NULL | |||||
); | |||||
} | |||||
return 0; | |||||
} | |||||
void IZ_TeardownMIDIInput(IZ_MIDIInputState(* state)[PLAYERS]) { | |||||
for (uint8_t i = 0; i < PLAYERS; i += 1) { | |||||
if (!state[i]->stream) { | |||||
continue; | |||||
} | |||||
Pm_Close(state[i]->stream); | |||||
} | |||||
} |
@@ -0,0 +1,58 @@ | |||||
#ifndef IZ_MIDI_H | |||||
#define IZ_MIDI_H | |||||
#include <string.h> | |||||
#include <portmidi.h> | |||||
#include <minIni.h> | |||||
#include "IZ_action.h" | |||||
#define MIDI_EVENT_BUFFER_SIZE 1024 | |||||
typedef uint8_t IZ_MIDINote; | |||||
static const uint8_t IZ_MIDI_NOTE_ON = 0x90u; | |||||
static const uint8_t IZ_MIDI_NOTE_OFF = 0x80u; | |||||
typedef struct { | |||||
PmDeviceID device_id; | |||||
IZ_MIDINote control_mapping[CONTROLS]; | |||||
} IZ_MIDIInputConfig; | |||||
typedef struct { | |||||
IZ_MIDIInputConfig config; | |||||
const PmDeviceInfo* device_info; | |||||
PmStream* stream; | |||||
PmEvent event_buffer[MIDI_EVENT_BUFFER_SIZE]; | |||||
} IZ_MIDIInputState; | |||||
static const IZ_MIDINote IZ_DEFAULT_MIDI_INPUT_CONTROLS[PLAYERS][CONTROLS] = { | |||||
{ | |||||
70, | |||||
72, | |||||
71, | |||||
69, | |||||
77, | |||||
76, | |||||
48, | |||||
49, | |||||
50, | |||||
51, | |||||
52, | |||||
53, | |||||
54, | |||||
55, | |||||
56, | |||||
57, | |||||
} | |||||
}; | |||||
IZ_ProcedureResult IZ_SaveMIDIInputConfig(const char*, IZ_MIDIInputState(*)[PLAYERS]); | |||||
void IZ_HandleMIDIInputEvents(PmEvent, IZ_MIDIInputState(*)[PLAYERS], IZ_Action(*)[PLAYERS]); | |||||
IZ_ProcedureResult IZ_InitializeMIDIInput(const char*, IZ_MIDIInputState(*)[PLAYERS]); | |||||
void IZ_TeardownMIDIInput(IZ_MIDIInputState(*)[PLAYERS]); | |||||
#endif |
@@ -1,43 +1,7 @@ | |||||
#include <SDL.h> | #include <SDL.h> | ||||
#include <stdbool.h> | |||||
#include "IZ_app.h" | #include "IZ_app.h" | ||||
int main(int argc, char* args[]) { | |||||
int main(int argc, char* argv[]) { | |||||
IZ_App app; | IZ_App app; | ||||
IZ_ProcedureResult result = IZ_InitializeApp(&app); | |||||
if (result) { | |||||
return result; | |||||
} | |||||
bool quit = false; | |||||
SDL_Event e; | |||||
while (true) { | |||||
uint64_t ticks = SDL_GetTicks64(); | |||||
// TODO do audio processing | |||||
// TODO do networking? | |||||
// process events | |||||
while (SDL_PollEvent(&e) != 0) { | |||||
if (e.type == SDL_QUIT) { | |||||
quit = true; | |||||
break; | |||||
} | |||||
for (uint8_t p = 0; p < PLAYERS; p += 1) { | |||||
IZ_HandleInputEvents(e, &(app.input_state[p])); | |||||
} | |||||
} | |||||
if (quit) { | |||||
break; | |||||
} | |||||
IZ_UpdateVideo(&app.video_state, &app.input_state, ticks); | |||||
} | |||||
IZ_TeardownApp(&app); | |||||
return 0; | |||||
return IZ_RunApp(&app, argc, argv); | |||||
} | } |
@@ -21,6 +21,8 @@ void IZ_LoadVideoConfig(const char* config_path, IZ_VideoConfig* config) { | |||||
} | } | ||||
IZ_ProcedureResult IZ_InitializeVideo(const char* config_path, IZ_VideoState* state) { | IZ_ProcedureResult IZ_InitializeVideo(const char* config_path, IZ_VideoState* state) { | ||||
*state = (IZ_VideoState) {}; | |||||
IZ_LoadVideoConfig(config_path, &state->config); | IZ_LoadVideoConfig(config_path, &state->config); | ||||
if (IZ_SaveVideoConfig(config_path, &state->config)) { | if (IZ_SaveVideoConfig(config_path, &state->config)) { | ||||
fprintf_s(stderr, "Error committing video config.\n"); | fprintf_s(stderr, "Error committing video config.\n"); | ||||
@@ -45,7 +47,7 @@ IZ_ProcedureResult IZ_InitializeVideo(const char* config_path, IZ_VideoState* st | |||||
return 0; | return 0; | ||||
} | } | ||||
void IZ_UpdateVideo(IZ_VideoState* video_state, IZ_InputState(* input_states)[PLAYERS], uint64_t ticks) { | |||||
void IZ_UpdateVideo(IZ_VideoState* video_state, IZ_InputState* input_states, uint64_t ticks) { | |||||
if (ticks - video_state->last_update_at > 1000 / video_state->config.max_fps) { | if (ticks - video_state->last_update_at > 1000 / video_state->config.max_fps) { | ||||
// Update window | // Update window | ||||
SDL_FillRect(video_state->surface, NULL, SDL_MapRGB(video_state->surface->format, 0x00, 0x00, 0x00)); | SDL_FillRect(video_state->surface, NULL, SDL_MapRGB(video_state->surface->format, 0x00, 0x00, 0x00)); | ||||
@@ -71,7 +73,7 @@ void IZ_UpdateVideo(IZ_VideoState* video_state, IZ_InputState(* input_states)[PL | |||||
const uint8_t row = i / 4; | const uint8_t row = i / 4; | ||||
const IZ_Action bitflag = (0x1 << i); | const IZ_Action bitflag = (0x1 << i); | ||||
const uint8_t size = 4; | const uint8_t size = 4; | ||||
if (input_states[p]->action & bitflag) { | |||||
if (input_states->action[p] & bitflag) { | |||||
SDL_FillRect(video_state->surface, &(SDL_Rect) { | SDL_FillRect(video_state->surface, &(SDL_Rect) { | ||||
column * size, | column * size, | ||||
row * size, | row * size, | ||||
@@ -27,7 +27,7 @@ IZ_ProcedureResult IZ_InitializeVideo(const char*, IZ_VideoState*); | |||||
IZ_ProcedureResult IZ_SaveVideoConfig(const char*, IZ_VideoConfig*); | IZ_ProcedureResult IZ_SaveVideoConfig(const char*, IZ_VideoConfig*); | ||||
void IZ_UpdateVideo(IZ_VideoState*, IZ_InputState(*)[PLAYERS], uint64_t); | |||||
void IZ_UpdateVideo(IZ_VideoState*, IZ_InputState*, uint64_t); | |||||
void IZ_TeardownVideo(IZ_VideoState*); | void IZ_TeardownVideo(IZ_VideoState*); | ||||