Implement event handling for gamepads and joysticks. In addition, the events are ordered according to SDL compatibility.feature/data-structs
@@ -6,10 +6,10 @@ | |||
typedef uint16_t IZ_Action; | |||
static const char* ACTION_NAMES[CONTROLS] = { | |||
"Up", | |||
"Right", | |||
"Down", | |||
"Left", | |||
"Up", | |||
"Affirm", | |||
"Negate", | |||
"Action0", | |||
@@ -24,4 +24,23 @@ static const char* ACTION_NAMES[CONTROLS] = { | |||
"Action9", | |||
}; | |||
typedef enum { | |||
IZ_ACTION_INDEX_UP, | |||
IZ_ACTION_INDEX_RIGHT, | |||
IZ_ACTION_INDEX_DOWN, | |||
IZ_ACTION_INDEX_LEFT, | |||
IZ_ACTION_INDEX_AFFIRM, | |||
IZ_ACTION_INDEX_NEGATE, | |||
IZ_ACTION_INDEX_ACTION0, | |||
IZ_ACTION_INDEX_ACTION1, | |||
IZ_ACTION_INDEX_ACTION2, | |||
IZ_ACTION_INDEX_ACTION3, | |||
IZ_ACTION_INDEX_ACTION4, | |||
IZ_ACTION_INDEX_ACTION5, | |||
IZ_ACTION_INDEX_ACTION6, | |||
IZ_ACTION_INDEX_ACTION7, | |||
IZ_ACTION_INDEX_ACTION8, | |||
IZ_ACTION_INDEX_ACTION9, | |||
} IZ_ActionIndex; | |||
#endif |
@@ -4,5 +4,10 @@ int IZ_InitializeApp(IZ_App* app) { | |||
IZ_LoadConfig(&app->config); | |||
IZ_SaveConfig(&app->config); | |||
for (uint8_t p = 0; p < PLAYERS; p += 1) { | |||
app->assigned_joysticks[p] = NULL; | |||
app->actions[p] = 0; | |||
} | |||
return 0; | |||
} |
@@ -1,10 +1,17 @@ | |||
#ifndef IZ_APP_H | |||
#define IZ_APP_H | |||
#include <SDL_joystick.h> | |||
#include "config/IZ_config.h" | |||
#include "IZ_action.h" | |||
typedef struct { | |||
IZ_Config config; | |||
IZ_Action actions[PLAYERS]; | |||
SDL_Joystick* assigned_joysticks[PLAYERS]; | |||
} IZ_App; | |||
int IZ_InitializeApp(IZ_App*); | |||
@@ -22,6 +22,11 @@ void IZ_SaveConfig(IZ_Config* config) { | |||
fprintf_s(fp, "[Video]\n"); | |||
fprintf_s(fp, "Width=%u\n", config->video.width); | |||
fprintf_s(fp, "Height=%u\n", config->video.height); | |||
fprintf_s(fp, "MaxFps=%u\n", config->video.max_fps); | |||
fprintf_s(fp, "\n"); | |||
fprintf_s(fp, "[Input]\n"); | |||
fprintf_s(fp, "GamepadAxisThreshold=%u\n", config->input.gamepad_axis_threshold); | |||
fprintf_s(fp, "\n"); | |||
for (uint8_t p = 0; p < PLAYERS; p += 1) { | |||
@@ -29,6 +34,12 @@ void IZ_SaveConfig(IZ_Config* config) { | |||
for (uint8_t i = 0; i < CONTROLS; i += 1) { | |||
fprintf_s(fp, "%s=%s\n", ACTION_NAMES[i], SDL_GetKeyName(config->controls[p].keyboard[i])); | |||
} | |||
fprintf_s(fp, "\n"); | |||
fprintf_s(fp, "[Controls.%u.Joystick]\n", p); | |||
for (uint8_t i = 4; i < CONTROLS; i += 1) { | |||
fprintf_s(fp, "%s=%d\n", ACTION_NAMES[i], config->controls[p].gamepad[i]); | |||
} | |||
} | |||
} | |||
@@ -37,11 +48,25 @@ void IZ_LoadConfig(IZ_Config* config) { | |||
IZ_GetConfigPath(config_path); | |||
config->video.width = ini_getl("Video", "Width", 640l, config_path); | |||
config->video.height = ini_getl("Video", "Height", 480l, config_path); | |||
config->video.max_fps = ini_getl("Video", "MaxFps", 30, config_path); | |||
config->input.gamepad_axis_threshold = ini_getl("Input", "GamepadAxisThreshold", 8000, config_path); | |||
char buffer[128]; | |||
char section_name[20] = "Controls.0.Keyboard"; | |||
for (uint8_t i = 0; i < CONTROLS; i += 1) { | |||
section_name[9] = (char) (48 + i); | |||
ini_gets(section_name, ACTION_NAMES[i], SDL_GetKeyName(IZ_DEFAULT_KEYBOARD_CONTROLS[i]), buffer, 128, config_path); | |||
config->controls[0].keyboard[i] = SDL_GetKeyFromName(buffer); | |||
char keyboard_section_name[20] = "Controls.0.Keyboard"; | |||
for (uint8_t p = 0; p < PLAYERS; p += 1) { | |||
keyboard_section_name[9] = (char) (48 + p); | |||
for (uint8_t i = 0; i < CONTROLS; i += 1) { | |||
ini_gets(keyboard_section_name, ACTION_NAMES[i], SDL_GetKeyName(IZ_DEFAULT_KEYBOARD_CONTROLS[p][i]), buffer, 128, | |||
config_path); | |||
config->controls[p].keyboard[i] = SDL_GetKeyFromName(buffer); | |||
} | |||
} | |||
char joystick_section_name[20] = "Controls.0.Joystick"; | |||
for (uint8_t p = 0; p < PLAYERS; p += 1) { | |||
joystick_section_name[9] = (char) (48 + p); | |||
for (uint8_t i = 4; i < CONTROLS; i += 1) { | |||
config->controls[p].gamepad[i] = ini_getl(joystick_section_name, ACTION_NAMES[i], IZ_DEFAULT_JOYSTICK_CONTROLS[p][i], config_path); | |||
} | |||
} | |||
} |
@@ -7,8 +7,13 @@ | |||
typedef struct { | |||
uint16_t width; | |||
uint16_t height; | |||
uint8_t max_fps; | |||
} IZ_VideoConfig; | |||
typedef struct { | |||
uint16_t gamepad_axis_threshold; | |||
} IZ_InputConfig; | |||
typedef SDL_KeyCode IZ_KeyCode; | |||
typedef int IZ_PadButton; | |||
@@ -20,26 +25,50 @@ typedef struct { | |||
typedef struct { | |||
IZ_VideoConfig video; | |||
IZ_InputConfig input; | |||
IZ_ControlsConfig controls[PLAYERS]; | |||
} IZ_Config; | |||
static const IZ_KeyCode IZ_DEFAULT_KEYBOARD_CONTROLS[CONTROLS] = { | |||
SDLK_RIGHT, | |||
SDLK_DOWN, | |||
SDLK_LEFT, | |||
SDLK_UP, | |||
SDLK_RETURN, // yes | |||
SDLK_BACKSPACE, // no | |||
SDLK_a, // action0 | |||
SDLK_s, // action1 | |||
SDLK_d, // action2 | |||
SDLK_f, // action3 | |||
SDLK_z, // action4 | |||
SDLK_x, // action5 | |||
SDLK_c, // action6 | |||
SDLK_v, // action7 | |||
SDLK_w, // action8 | |||
SDLK_e, // action9 | |||
static const IZ_KeyCode IZ_DEFAULT_KEYBOARD_CONTROLS[PLAYERS][CONTROLS] = { | |||
{ | |||
SDLK_UP, | |||
SDLK_RIGHT, | |||
SDLK_DOWN, | |||
SDLK_LEFT, | |||
SDLK_RETURN, // yes | |||
SDLK_BACKSPACE, // no | |||
SDLK_a, // action0 | |||
SDLK_s, // action1 | |||
SDLK_d, // action2 | |||
SDLK_f, // action3 | |||
SDLK_z, // action4 | |||
SDLK_x, // action5 | |||
SDLK_c, // action6 | |||
SDLK_v, // action7 | |||
SDLK_w, // action8 | |||
SDLK_e, // action9 | |||
}, | |||
}; | |||
static const uint8_t IZ_DEFAULT_JOYSTICK_CONTROLS[PLAYERS][CONTROLS] = { | |||
{ | |||
255, | |||
255, | |||
255, | |||
255, | |||
11, | |||
10, | |||
1, | |||
0, | |||
4, | |||
3, | |||
6, | |||
7, | |||
8, | |||
9, | |||
13, | |||
14, | |||
}, | |||
}; | |||
void IZ_GetConfigPath(char* config_path); | |||
@@ -39,7 +39,6 @@ int main(int argc, char* args[]) { | |||
SDL_Event e; | |||
screen_surface = SDL_GetWindowSurface(window); | |||
IZ_Action action = 0; | |||
while (!quit) { | |||
SDL_FillRect(screen_surface, NULL, SDL_MapRGB(screen_surface->format, 0x00, 0x00, 0x00)); | |||
uint64_t ticks = SDL_GetTicks64(); | |||
@@ -63,24 +62,89 @@ int main(int argc, char* args[]) { | |||
quit = true; | |||
} | |||
for (uint8_t i = 0; i < CONTROLS; i += 1) { | |||
// TODO do same for gamepad | |||
if (e.key.keysym.sym == app.config.controls[0].keyboard[i]) { | |||
const uint16_t bitflag = (0x1 << i); | |||
if (e.type == SDL_KEYDOWN) { | |||
action |= bitflag; | |||
} else if (e.type == SDL_KEYUP) { | |||
action &= ~bitflag; | |||
// Handle joystick events | |||
for (uint8_t current_player = 0; current_player < PLAYERS; current_player += 1) { | |||
if (e.type == SDL_JOYDEVICEADDED) { | |||
if (SDL_NumJoysticks() <= PLAYERS && !app.assigned_joysticks[current_player]) { | |||
app.assigned_joysticks[current_player] = SDL_JoystickOpen(e.jdevice.which); | |||
} | |||
} | |||
if (e.type == SDL_JOYDEVICEREMOVED) { | |||
if ( | |||
app.assigned_joysticks[current_player] | |||
&& SDL_JoystickInstanceID(app.assigned_joysticks[current_player]) == e.jdevice.which | |||
) { | |||
app.assigned_joysticks[current_player] = NULL; | |||
} | |||
} | |||
if (e.type == SDL_JOYAXISMOTION) { | |||
if (e.jaxis.axis == 0 || e.jaxis.axis == 3) { | |||
app.actions[current_player] &= ~(0x1 << IZ_ACTION_INDEX_RIGHT); | |||
app.actions[current_player] &= ~(0x1 << IZ_ACTION_INDEX_LEFT); | |||
if (e.jaxis.value > app.config.input.gamepad_axis_threshold) { | |||
app.actions[current_player] |= (0x1 << IZ_ACTION_INDEX_RIGHT); | |||
} else if (e.jaxis.value <= -app.config.input.gamepad_axis_threshold) { | |||
app.actions[current_player] |= (0x1 << IZ_ACTION_INDEX_LEFT); | |||
} | |||
} | |||
if (e.jaxis.axis == 1 || e.jaxis.axis == 4) { | |||
app.actions[current_player] &= ~(0x1 << IZ_ACTION_INDEX_UP); | |||
app.actions[current_player] &= ~(0x1 << IZ_ACTION_INDEX_DOWN); | |||
if (e.jaxis.value > app.config.input.gamepad_axis_threshold) { | |||
app.actions[current_player] |= (0x1 << IZ_ACTION_INDEX_DOWN); | |||
} else if (e.jaxis.value <= -app.config.input.gamepad_axis_threshold) { | |||
app.actions[current_player] |= (0x1 << IZ_ACTION_INDEX_UP); | |||
} | |||
} | |||
} | |||
if (e.type == SDL_JOYHATMOTION) { | |||
app.actions[current_player] &= ~(0x1 << IZ_ACTION_INDEX_UP); | |||
app.actions[current_player] &= ~(0x1 << IZ_ACTION_INDEX_RIGHT); | |||
app.actions[current_player] &= ~(0x1 << IZ_ACTION_INDEX_DOWN); | |||
app.actions[current_player] &= ~(0x1 << IZ_ACTION_INDEX_LEFT); | |||
if (e.jhat.value != 0) { | |||
app.actions[current_player] |= e.jhat.value; | |||
} | |||
} | |||
} | |||
for (unsigned char i = 0; i < CONTROLS; i += 1) { | |||
// Handle keyboard events | |||
for (uint8_t current_player = 0; current_player < PLAYERS; current_player += 1) { | |||
for (uint8_t i = 0; i < CONTROLS; i += 1) { | |||
if (e.key.keysym.sym == app.config.controls[current_player].keyboard[i]) { | |||
const uint16_t bitflag = (0x1 << i); | |||
if (e.type == SDL_KEYDOWN) { | |||
app.actions[current_player] |= bitflag; | |||
} else if (e.type == SDL_KEYUP) { | |||
app.actions[current_player] &= ~bitflag; | |||
} | |||
} | |||
if (i >= 4) { | |||
if (e.jbutton.button == app.config.controls[current_player].gamepad[i]) { | |||
const uint16_t bitflag = (0x1 << i); | |||
if (e.type == SDL_JOYBUTTONDOWN) { | |||
app.actions[current_player] |= bitflag; | |||
} else if (e.type == SDL_JOYBUTTONUP) { | |||
app.actions[current_player] &= ~bitflag; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
// Update window | |||
for (uint8_t current_player = 0; current_player < PLAYERS; current_player += 1) { | |||
for (uint8_t i = 0; i < CONTROLS; i += 1) { | |||
const uint8_t column = 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; | |||
if (action & bitflag) { | |||
if (app.actions[current_player] & bitflag) { | |||
SDL_FillRect(screen_surface, &(SDL_Rect) { | |||
column * size, | |||
row * size, | |||
@@ -89,9 +153,9 @@ int main(int argc, char* args[]) { | |||
}, SDL_MapRGB(screen_surface->format, 0xff, 0xff, 0x00)); | |||
} | |||
} | |||
SDL_UpdateWindowSurface(window); | |||
} | |||
SDL_UpdateWindowSurface(window); | |||
} | |||
SDL_DestroyWindow(window); | |||
SDL_Quit(); | |||