Implement event handling for gamepads and joysticks. In addition, the events are ordered according to SDL compatibility.master
@@ -6,10 +6,10 @@ | |||||
typedef uint16_t IZ_Action; | typedef uint16_t IZ_Action; | ||||
static const char* ACTION_NAMES[CONTROLS] = { | static const char* ACTION_NAMES[CONTROLS] = { | ||||
"Up", | |||||
"Right", | "Right", | ||||
"Down", | "Down", | ||||
"Left", | "Left", | ||||
"Up", | |||||
"Affirm", | "Affirm", | ||||
"Negate", | "Negate", | ||||
"Action0", | "Action0", | ||||
@@ -24,4 +24,23 @@ static const char* ACTION_NAMES[CONTROLS] = { | |||||
"Action9", | "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 | #endif |
@@ -4,5 +4,10 @@ int IZ_InitializeApp(IZ_App* app) { | |||||
IZ_LoadConfig(&app->config); | IZ_LoadConfig(&app->config); | ||||
IZ_SaveConfig(&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; | return 0; | ||||
} | } |
@@ -1,10 +1,17 @@ | |||||
#ifndef IZ_APP_H | #ifndef IZ_APP_H | ||||
#define IZ_APP_H | #define IZ_APP_H | ||||
#include <SDL_joystick.h> | |||||
#include "config/IZ_config.h" | #include "config/IZ_config.h" | ||||
#include "IZ_action.h" | |||||
typedef struct { | typedef struct { | ||||
IZ_Config config; | IZ_Config config; | ||||
IZ_Action actions[PLAYERS]; | |||||
SDL_Joystick* assigned_joysticks[PLAYERS]; | |||||
} IZ_App; | } IZ_App; | ||||
int IZ_InitializeApp(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, "[Video]\n"); | ||||
fprintf_s(fp, "Width=%u\n", config->video.width); | fprintf_s(fp, "Width=%u\n", config->video.width); | ||||
fprintf_s(fp, "Height=%u\n", config->video.height); | 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"); | fprintf_s(fp, "\n"); | ||||
for (uint8_t p = 0; p < PLAYERS; p += 1) { | 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) { | 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, "%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); | IZ_GetConfigPath(config_path); | ||||
config->video.width = ini_getl("Video", "Width", 640l, config_path); | config->video.width = ini_getl("Video", "Width", 640l, config_path); | ||||
config->video.height = ini_getl("Video", "Height", 480l, 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 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 { | typedef struct { | ||||
uint16_t width; | uint16_t width; | ||||
uint16_t height; | uint16_t height; | ||||
uint8_t max_fps; | |||||
} IZ_VideoConfig; | } IZ_VideoConfig; | ||||
typedef struct { | |||||
uint16_t gamepad_axis_threshold; | |||||
} IZ_InputConfig; | |||||
typedef SDL_KeyCode IZ_KeyCode; | typedef SDL_KeyCode IZ_KeyCode; | ||||
typedef int IZ_PadButton; | typedef int IZ_PadButton; | ||||
@@ -20,26 +25,50 @@ typedef struct { | |||||
typedef struct { | typedef struct { | ||||
IZ_VideoConfig video; | IZ_VideoConfig video; | ||||
IZ_InputConfig input; | |||||
IZ_ControlsConfig controls[PLAYERS]; | IZ_ControlsConfig controls[PLAYERS]; | ||||
} IZ_Config; | } 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); | void IZ_GetConfigPath(char* config_path); | ||||
@@ -39,7 +39,6 @@ int main(int argc, char* args[]) { | |||||
SDL_Event e; | SDL_Event e; | ||||
screen_surface = SDL_GetWindowSurface(window); | screen_surface = SDL_GetWindowSurface(window); | ||||
IZ_Action action = 0; | |||||
while (!quit) { | while (!quit) { | ||||
SDL_FillRect(screen_surface, NULL, SDL_MapRGB(screen_surface->format, 0x00, 0x00, 0x00)); | SDL_FillRect(screen_surface, NULL, SDL_MapRGB(screen_surface->format, 0x00, 0x00, 0x00)); | ||||
uint64_t ticks = SDL_GetTicks64(); | uint64_t ticks = SDL_GetTicks64(); | ||||
@@ -63,24 +62,89 @@ int main(int argc, char* args[]) { | |||||
quit = true; | 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 column = i % 4; | ||||
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 (action & bitflag) { | |||||
if (app.actions[current_player] & bitflag) { | |||||
SDL_FillRect(screen_surface, &(SDL_Rect) { | SDL_FillRect(screen_surface, &(SDL_Rect) { | ||||
column * size, | column * size, | ||||
row * size, | row * size, | ||||
@@ -89,9 +153,9 @@ int main(int argc, char* args[]) { | |||||
}, SDL_MapRGB(screen_surface->format, 0xff, 0xff, 0x00)); | }, SDL_MapRGB(screen_surface->format, 0xff, 0xff, 0x00)); | ||||
} | } | ||||
} | } | ||||
SDL_UpdateWindowSurface(window); | |||||
} | } | ||||
SDL_UpdateWindowSurface(window); | |||||
} | } | ||||
SDL_DestroyWindow(window); | SDL_DestroyWindow(window); | ||||
SDL_Quit(); | SDL_Quit(); | ||||