-
Notifications
You must be signed in to change notification settings - Fork 21
Add basic gamepad support and button remapping #35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Changes from 10 commits
24fd6f8
f324e8e
d2d579e
383bef2
d820241
3716a5c
afa49d4
8fcf1e8
dbbebdb
c4b11c3
1f3f9f2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| { | ||
| "configurations": [ | ||
| { | ||
| "name": "Linux", | ||
| "includePath": [ | ||
| "${workspaceFolder}/**", | ||
| "/usr/include/SDL2" | ||
| ], | ||
| "defines": [], | ||
| "compilerPath": "/usr/bin/gcc", | ||
| "cStandard": "c17", | ||
| "cppStandard": "gnu++17", | ||
| "intelliSenseMode": "linux-gcc-x64" | ||
| } | ||
| ], | ||
| "version": 4 | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| // | ||
| // Gamepad Interface | ||
| // | ||
|
|
||
| #ifndef __GAMEPAD_H__ | ||
| #define __GAMEPAD_H__ | ||
|
|
||
| void Gamepad_Init(void); | ||
| void Gamepad_Update(void); | ||
| void Gamepad_CheckState(void); | ||
|
|
||
| extern int btnbindings[NUM_KEYS]; | ||
|
||
|
|
||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| #include <SDL.h> | ||
| #include "video.h" | ||
|
|
||
| SDL_GameController *gamepad = NULL; | ||
| static int gamepad_initted = 0; | ||
|
|
||
| int btnbindings[NUM_KEYS] = { | ||
| -1, // KEY_UNKNOWN | ||
| SDL_CONTROLLER_BUTTON_DPAD_LEFT, // KEY_PULLUP | ||
| SDL_CONTROLLER_BUTTON_DPAD_RIGHT, // KEY_PULLDOWN | ||
| SDL_CONTROLLER_BUTTON_Y, // KEY_FLIP | ||
| SDL_CONTROLLER_BUTTON_A, // KEY_BOMB | ||
| SDL_CONTROLLER_BUTTON_X, // KEY_FIRE | ||
| SDL_CONTROLLER_BUTTON_START, // KEY_HOME | ||
| SDL_CONTROLLER_BUTTON_LEFTSTICK, // KEY_MISSILE | ||
| SDL_CONTROLLER_BUTTON_RIGHTSTICK, // KEY_STARBURST | ||
| SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,// KEY_ACCEL | ||
| SDL_CONTROLLER_BUTTON_LEFTSHOULDER, // KEY_DECEL | ||
| SDL_CONTROLLER_BUTTON_BACK, // KEY_SOUND | ||
| }; | ||
|
|
||
| void Gamepad_Init(void) | ||
| { | ||
| if (SDL_Init(SDL_INIT_GAMECONTROLLER) < 0) { | ||
| printf("Gamepad could not initialize! SDL_Error: %s\n", SDL_GetError()); | ||
| } else { | ||
| if (SDL_NumJoysticks() > 0) { | ||
| gamepad = SDL_GameControllerOpen(0); | ||
| } | ||
| } | ||
|
|
||
| gamepad_initted = 1; | ||
| } | ||
|
|
||
| void Gamepad_Update(void) { | ||
| if (!gamepad_initted || gamepad == NULL) { | ||
| Gamepad_Init(); | ||
|
||
| return; | ||
| } | ||
|
|
||
| for (int i = 1; i < NUM_KEYS; ++i) { | ||
| if (btnbindings[i] != -1) { | ||
| if (SDL_GameControllerGetButton(gamepad, btnbindings[i])) { | ||
| keysdown[i] |= 3; // Button is pressed | ||
| } else { | ||
| keysdown[i] &= ~1; // Button is not pressed | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| void Gamepad_Shutdown(void) | ||
| { | ||
| if (!gamepad_initted) { | ||
| return; | ||
| } | ||
| // Close the gamepad | ||
|
||
| if (gamepad != NULL) { | ||
| SDL_GameControllerClose(gamepad); | ||
| gamepad = NULL; | ||
| } | ||
|
|
||
| gamepad_initted = 0; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,10 +19,12 @@ | |
| #include <errno.h> | ||
| #include <stdio.h> | ||
| #include <string.h> | ||
| #include <SDL.h> | ||
|
|
||
| #include "timer.h" | ||
| #include "pcsound.h" | ||
| #include "video.h" | ||
| #include "gamepad.h" | ||
|
|
||
| #include "swconf.h" | ||
| #include "swend.h" | ||
|
|
@@ -82,6 +84,17 @@ static confoption_t confoptions[] = { | |
| {"key_home", CONF_KEY, {&keybindings[KEY_HOME]}}, | ||
| {"key_missile", CONF_KEY, {&keybindings[KEY_MISSILE]}}, | ||
| {"key_starburst", CONF_KEY, {&keybindings[KEY_STARBURST]}}, | ||
|
|
||
| {"btn_accelerate", CONF_BTN, {&btnbindings[KEY_ACCEL]}}, | ||
| {"btn_decelerate", CONF_BTN, {&btnbindings[KEY_DECEL]}}, | ||
| {"btn_pullup", CONF_BTN, {&btnbindings[KEY_PULLUP]}}, | ||
| {"btn_pulldown", CONF_BTN, {&btnbindings[KEY_PULLDOWN]}}, | ||
| {"btn_flip", CONF_BTN, {&btnbindings[KEY_FLIP]}}, | ||
| {"btn_fire", CONF_BTN, {&btnbindings[KEY_FIRE]}}, | ||
| {"btn_dropbomb", CONF_BTN, {&btnbindings[KEY_BOMB]}}, | ||
| {"btn_home", CONF_BTN, {&btnbindings[KEY_HOME]}}, | ||
| {"btn_missile", CONF_BTN, {&btnbindings[KEY_MISSILE]}}, | ||
| {"btn_starburst", CONF_BTN, {&btnbindings[KEY_STARBURST]}}, | ||
| }; | ||
|
|
||
| static int num_confoptions = sizeof(confoptions) / sizeof(*confoptions); | ||
|
|
@@ -111,6 +124,7 @@ static void parse_config_line(char *config_file, int lineno, char *line) | |
| { | ||
| char *name, *value, *p; | ||
| int key; | ||
| int btn; | ||
| confoption_t *opt; | ||
|
|
||
| p = line; | ||
|
|
@@ -169,6 +183,11 @@ static void parse_config_line(char *config_file, int lineno, char *line) | |
| *opt->value.i = key; | ||
| } | ||
| break; | ||
| case CONF_BTN: | ||
| if (sscanf(value, "%d", &btn) == 1) { | ||
| *opt->value.i = btn; | ||
| } | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
|
|
@@ -246,6 +265,7 @@ void swsaveconf(void) | |
| break; | ||
| case CONF_INT: | ||
| case CONF_KEY: | ||
| case CONF_BTN: | ||
| fprintf(fs, "%d", *confoptions[i].value.i); | ||
| break; | ||
| default: | ||
|
|
@@ -308,6 +328,53 @@ static void change_key_binding(struct menuitem *item) | |
| *opt->value.i = key; | ||
| } | ||
|
|
||
| static void change_btn_binding(struct menuitem *item) | ||
| { | ||
| confoption_t *opt; | ||
| int btn = -1; | ||
| SDL_Event event; | ||
|
|
||
| Vid_ClearBuf(); | ||
|
|
||
| swcolor(3); | ||
| swposcur(10, 5); | ||
| swputs("Press the new button for: "); | ||
|
|
||
| swcolor(2); | ||
| swposcur(14, 7); | ||
| swputs(item->description); | ||
|
|
||
| swcolor(1); | ||
| swposcur(1, 22); | ||
| swputs(" ESC - Cancel"); | ||
|
|
||
| Vid_Update(); | ||
|
|
||
| while (btn == -1) { | ||
| // Have to use SDL_WaitEventTimeout() instead Timer_Sleep(50) so that buttons register | ||
| if (SDL_WaitEventTimeout(&event, 10)) { | ||
| if (event.type == SDL_CONTROLLERBUTTONDOWN) { | ||
| btn = event.cbutton.button; | ||
| break; | ||
| } else if (event.type == SDL_KEYDOWN) { | ||
| if (event.key.keysym.sym == SDLK_ESCAPE) { | ||
| return; | ||
| } | ||
| } | ||
| } | ||
| swsndupdate(); | ||
| if (ctlbreak()) { | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| opt = confoption_by_name(item->config_name); | ||
| if (opt == NULL) { | ||
| return; | ||
| } | ||
| *opt->value.i = btn; | ||
| } | ||
|
|
||
| static void drawmenu(char *title, struct menuitem *menu) | ||
| { | ||
| int i, y, keynum, said_key = 0; | ||
|
|
@@ -372,6 +439,12 @@ static void drawmenu(char *title, struct menuitem *menu) | |
| case CONF_KEY: | ||
| swputs(Vid_KeyName(*opt->value.i)); | ||
| break; | ||
| case CONF_BTN: { | ||
| char btnString[20]; // Large enough to hold any integer | ||
| sprintf(btnString, "%d", *opt->value.i); // Convert integer to string | ||
| swputs(btnString); | ||
| break; | ||
| } | ||
|
||
| default: | ||
| break; | ||
| } | ||
|
|
@@ -455,6 +528,9 @@ static int runmenu(char *title, struct menuitem *menu) | |
| case CONF_KEY: | ||
| change_key_binding(pressed); | ||
| break; | ||
| case CONF_BTN: | ||
| change_btn_binding(pressed); | ||
| break; | ||
| default: | ||
| break; | ||
| } | ||
|
|
@@ -480,6 +556,18 @@ static struct menuitem keys_menu[] = { | |
| {NULL}, | ||
| }; | ||
|
|
||
| static struct menuitem btns_menu[] = { | ||
| {"btn_accelerate", "Accelerate"}, | ||
| {"btn_decelerate", "Decelerate"}, | ||
| {"btn_pullup", "Pull up"}, | ||
| {"btn_pulldown", "Pull down"}, | ||
| {"btn_flip", "Flip"}, | ||
| {"btn_fire", "Fire machine gun"}, | ||
| {"btn_dropbomb", "Drop bomb"}, | ||
| {"btn_home", "Navigate home"}, | ||
| {NULL}, | ||
| }; | ||
|
|
||
| static struct menuitem options_menu[] = { | ||
| {"vid_fullscreen", "Run fullscreen"}, | ||
| {"conf_video_palette", "Video palette"}, | ||
|
|
@@ -492,6 +580,7 @@ static struct menuitem options_menu[] = { | |
| {"conf_harrykeys", "Harry keys mode"}, | ||
| {"", ""}, | ||
| {">K", "Key bindings"}, | ||
| {">G", "Gamepad bindings"}, | ||
| {NULL}, | ||
| }; | ||
|
|
||
|
|
@@ -504,6 +593,9 @@ void setconfig(void) | |
| case 'K': | ||
| runmenu("OPTIONS > KEY BINDINGS", keys_menu); | ||
| break; | ||
| case 'G': | ||
| runmenu("OPTIONS > GAMEPAD BINDINGS", btns_menu); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please move
swconf.candswinit.cback to their previous location, they don't belong in the sdl/ subdirectory.I understand why you had to do this: the new feature requires using new SDL functions. But the correct way to do this is to create accessor functions to access them. For example, we have
Vid_GetKey()that is used by the configuration code to read a keypress. You need a similar function that yourchange_btn_bindingfunction can call to read a gamepad press.In case it's not clear: the code in src/ is the "core code" of the game. The idea is that if someone wants to port the game to a new system where SDL isn't available, they should just be able to reimplement the files in src/sdl/ . Think of it as though there were multiple other directories alongside src/sdl/ - we wouldn't want to contain duplicate implementations of the initialization and configuration code for every one of them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad - I should've asked in Discord for some more context. This is all moved back and straightened out.