Browse Source

Add gamepad/joystick support

Implement event handling for gamepads and joysticks.

In addition, the events are ordered according to SDL compatibility.
master
TheoryOfNekomata 2 years ago
parent
commit
cc63e9e338
6 changed files with 186 additions and 37 deletions
  1. +20
    -1
      src/packages/game/IZ_action.h
  2. +5
    -0
      src/packages/game/IZ_app.c
  3. +7
    -0
      src/packages/game/IZ_app.h
  4. +30
    -5
      src/packages/game/config/IZ_config.c
  5. +46
    -17
      src/packages/game/config/IZ_config.h
  6. +78
    -14
      src/packages/game/main.c

+ 20
- 1
src/packages/game/IZ_action.h View File

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

+ 5
- 0
src/packages/game/IZ_app.c View File

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

+ 7
- 0
src/packages/game/IZ_app.h View File

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


+ 30
- 5
src/packages/game/config/IZ_config.c View File

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

+ 46
- 17
src/packages/game/config/IZ_config.h View File

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


+ 78
- 14
src/packages/game/main.c View File

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


Loading…
Cancel
Save