summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Pavone <pavone@retrodev.com>2017-01-22 16:23:59 -0800
committerMichael Pavone <pavone@retrodev.com>2017-01-22 16:23:59 -0800
commitf7f8da113c0c2bef82101689aa7064066c3f43f5 (patch)
tree910c98128a11592340de0b65dd85b246f79c1c03
parent3f8d292a993973c0eb56348493c466910b528609 (diff)
Initial support for using SDL2 game controller mapping functionality
-rw-r--r--default.cfg35
-rw-r--r--io.c73
-rw-r--r--render.h9
-rwxr-xr-xrender_sdl.c64
4 files changed, 144 insertions, 37 deletions
diff --git a/default.cfg b/default.cfg
index 920a914..ef88166 100644
--- a/default.cfg
+++ b/default.cfg
@@ -42,14 +42,15 @@ bindings {
}
}
buttons {
- 0 gamepads.1.a
- 1 gamepads.1.b
- 2 gamepads.1.c
- 3 gamepads.1.x
- 4 gamepads.1.y
- 5 gamepads.1.z
- 6 gamepads.1.mode
- 7 gamepads.1.start
+ a gamepads.1.a
+ b gamepads.1.b
+ rightshoulder gamepads.1.c
+ x gamepads.1.x
+ y gamepads.1.y
+ leftshoulder gamepads.1.z
+ back gamepads.1.mode
+ start gamepads.1.start
+ guide ui.exit
}
}
1 {
@@ -62,14 +63,16 @@ bindings {
}
}
buttons {
- 0 gamepads.2.a
- 1 gamepads.2.b
- 2 gamepads.2.c
- 3 gamepads.2.x
- 4 gamepads.2.y
- 5 gamepads.2.z
- 6 gamepads.2.mode
- 7 gamepads.2.start
+ #this is exactly the same mapping as above, but with PS4 style names
+ cross gamepads.2.a
+ circle gamepads.2.b
+ r1 gamepads.2.c
+ square gamepads.2.x
+ triangle gamepads.2.y
+ l1 gamepads.2.z
+ share gamepads.2.mode
+ options gamepads.2.start
+ guide ui.exit
}
}
}
diff --git a/io.c b/io.c
index acc41b8..845c03a 100644
--- a/io.c
+++ b/io.c
@@ -923,6 +923,49 @@ void process_mouse(char *mousenum, tern_val value, void *data)
}
}
+typedef struct {
+ int padnum;
+ tern_node *padbuttons;
+ tern_node *mousebuttons;
+} pad_button_state;
+
+void process_pad_button(char *key, tern_val val, void *data)
+{
+ pad_button_state *state = data;
+ int hostpadnum = state->padnum;
+ int ui_func, padnum, button;
+ int bindtype = parse_binding_target(val.ptrval, state->padbuttons, state->mousebuttons, &ui_func, &padnum, &button);
+ char *end;
+ long hostbutton = strtol(key, &end, 10);
+ if (*end) {
+ //key is not a valid base 10 integer
+ hostbutton = render_translate_input_name(hostpadnum, key);
+ if (hostbutton < 0) {
+ if (hostbutton == RENDER_INVALID_NAME) {
+ warning("%s is not a valid gamepad input name\n", key);
+ } else if (hostbutton == RENDER_NOT_MAPPED) {
+ warning("No mapping exists for input %s on gamepad %d\n", key, hostpadnum);
+ }
+ return;
+ }
+ if (hostbutton & RENDER_DPAD_BIT) {
+ if (bindtype == BIND_GAMEPAD1) {
+ bind_dpad_gamepad(hostpadnum, render_dpad_part(hostbutton), render_direction_part(hostbutton), padnum, button);
+ } else {
+ bind_dpad_ui(hostpadnum, render_dpad_part(hostbutton), render_direction_part(hostbutton), ui_func, button);
+ }
+ } else if (hostbutton & RENDER_AXIS_BIT) {
+ //TODO: support analog axes
+ return;
+ }
+ }
+ if (bindtype == BIND_GAMEPAD1) {
+ bind_button_gamepad(hostpadnum, hostbutton, padnum, button);
+ } else if (bindtype == BIND_UI) {
+ bind_button_ui(hostpadnum, hostbutton, ui_func, button);
+ }
+}
+
void set_keybindings(sega_io *io)
{
static uint8_t already_done;
@@ -988,6 +1031,9 @@ void set_keybindings(sega_io *io)
mousebuttons = tern_insert_int(mousebuttons, ".start", MOUSE_START);
mousebuttons = tern_insert_int(mousebuttons, ".motion", PSEUDO_BUTTON_MOTION);
+ //pump event loop, so initial joystick insertion events are processed
+ process_events();
+
tern_node * keys = tern_get_node(tern_find_path(config, "bindings\0keys\0"));
process_keys(keys, special, padbuttons, mousebuttons, NULL);
char numstr[] = "00";
@@ -1030,26 +1076,13 @@ void set_keybindings(sega_io *io)
}
tern_node *button_node = tern_find_ptr(pad, "buttons");
if (button_node) {
- for (int but = 0; but < 30; but++)
- {
- if (but < 10) {
- numstr[0] = but + '0';
- numstr[1] = 0;
- } else {
- numstr[0] = but/10 + '0';
- numstr[1] = but%10 + '0';
- }
- char * target = tern_find_ptr(button_node, numstr);
- if (target) {
- int ui_func, padnum, button;
- int bindtype = parse_binding_target(target, padbuttons, mousebuttons, &ui_func, &padnum, &button);
- if (bindtype == BIND_GAMEPAD1) {
- bind_button_gamepad(i, but, padnum, button);
- } else if (bindtype == BIND_UI) {
- bind_button_ui(i, but, ui_func, button);
- }
- }
- }
+ pad_button_state state = {
+ .padnum = i,
+ .padbuttons = padbuttons,
+ .mousebuttons = mousebuttons
+ };
+ tern_foreach(button_node, process_pad_button, &state);
+
}
}
}
diff --git a/render.h b/render.h
index 33e3aa3..058d7a5 100644
--- a/render.h
+++ b/render.h
@@ -63,6 +63,12 @@ typedef enum {
NUM_VID_STD
} vid_std;
+#define RENDER_DPAD_BIT 0x40000000
+#define RENDER_AXIS_BIT 0x20000000
+#define RENDER_INVALID_NAME -1
+#define RENDER_NOT_MAPPED -2
+#define RENDER_NOT_PLUGGED_IN -3
+
uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b);
uint32_t *render_get_framebuffer(uint8_t which, int *pitch);
void render_framebuffer_updated(uint8_t which, int width);
@@ -81,6 +87,9 @@ int render_width();
int render_height();
int render_fullscreen();
void process_events();
+int32_t render_translate_input_name(int32_t controller, char *name);
+int32_t render_dpad_part(int32_t input);
+uint8_t render_direction_part(int32_t input);
void render_errorbox(char *title, char *message);
void render_warnbox(char *title, char *message);
void render_infobox(char *title, char *message);
diff --git a/render_sdl.c b/render_sdl.c
index e4aa38f..337d847 100755
--- a/render_sdl.c
+++ b/render_sdl.c
@@ -111,6 +111,7 @@ static void render_close_audio()
}
static SDL_Joystick * joysticks[MAX_JOYSTICKS];
+static int joystick_sdl_index[MAX_JOYSTICKS];
int render_width()
{
@@ -271,7 +272,7 @@ static vid_std video_standard = VID_NTSC;
static char *vid_std_names[NUM_VID_STD] = {"ntsc", "pal"};
void render_init(int width, int height, char * title, uint8_t fullscreen)
{
- if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0) {
+ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) {
fatal_error("Unable to init SDL: %s\n", SDL_GetError());
}
atexit(SDL_Quit);
@@ -626,6 +627,66 @@ static int lowest_unused_joystick_index()
return -1;
}
+int32_t render_translate_input_name(int32_t controller, char *name)
+{
+ static tern_node *button_lookup;
+ if (controller > MAX_JOYSTICKS || !joysticks[controller]) {
+ return RENDER_NOT_PLUGGED_IN;
+ }
+
+ if (!SDL_IsGameController(joystick_sdl_index[controller])) {
+ return RENDER_NOT_MAPPED;
+ }
+
+ if (!button_lookup) {
+ for (int i = SDL_CONTROLLER_BUTTON_A; i < SDL_CONTROLLER_BUTTON_MAX; i++)
+ {
+ button_lookup = tern_insert_int(button_lookup, SDL_GameControllerGetStringForButton(i), i);
+ }
+ //alternative Playstation-style names
+ button_lookup = tern_insert_int(button_lookup, "cross", SDL_CONTROLLER_BUTTON_A);
+ button_lookup = tern_insert_int(button_lookup, "circle", SDL_CONTROLLER_BUTTON_B);
+ button_lookup = tern_insert_int(button_lookup, "square", SDL_CONTROLLER_BUTTON_X);
+ button_lookup = tern_insert_int(button_lookup, "triangle", SDL_CONTROLLER_BUTTON_Y);
+ button_lookup = tern_insert_int(button_lookup, "share", SDL_CONTROLLER_BUTTON_BACK);
+ button_lookup = tern_insert_int(button_lookup, "select", SDL_CONTROLLER_BUTTON_BACK);
+ button_lookup = tern_insert_int(button_lookup, "options", SDL_CONTROLLER_BUTTON_START);
+ button_lookup = tern_insert_int(button_lookup, "l1", SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
+ button_lookup = tern_insert_int(button_lookup, "r1", SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
+ }
+ intptr_t sdl_button = tern_find_int(button_lookup, name, SDL_CONTROLLER_BUTTON_INVALID);
+ if (sdl_button == SDL_CONTROLLER_BUTTON_INVALID) {
+ return RENDER_INVALID_NAME;
+ }
+ SDL_GameController *control = SDL_GameControllerOpen(joystick_sdl_index[controller]);
+ if (!control) {
+ warning("Failed to open game controller %d: %s\n", controller, SDL_GetError());
+ return RENDER_NOT_PLUGGED_IN;
+ }
+ SDL_GameControllerButtonBind cbind = SDL_GameControllerGetBindForButton(control, sdl_button);
+ SDL_GameControllerClose(control);
+ switch (cbind.bindType)
+ {
+ case SDL_CONTROLLER_BINDTYPE_BUTTON:
+ return cbind.value.button;
+ case SDL_CONTROLLER_BINDTYPE_AXIS:
+ return RENDER_AXIS_BIT | cbind.value.axis;
+ case SDL_CONTROLLER_BINDTYPE_HAT:
+ return RENDER_DPAD_BIT | (cbind.value.hat.hat << 4) | cbind.value.hat.hat_mask;
+ }
+ return RENDER_NOT_MAPPED;
+}
+
+int32_t render_dpad_part(int32_t input)
+{
+ return input >> 4 & 0x7FFFFF;
+}
+
+uint8_t render_direction_part(int32_t input)
+{
+ return input & 0xF;
+}
+
static uint8_t scancode_map[SDL_NUM_SCANCODES] = {
[SDL_SCANCODE_A] = 0x1C,
[SDL_SCANCODE_B] = 0x32,
@@ -753,6 +814,7 @@ static int32_t handle_event(SDL_Event *event)
int index = lowest_unused_joystick_index();
if (index >= 0) {
SDL_Joystick * joy = joysticks[index] = SDL_JoystickOpen(event->jdevice.which);
+ joystick_sdl_index[index] = event->jdevice.which;
if (joy) {
printf("Joystick %d added: %s\n", index, SDL_JoystickName(joy));
printf("\tNum Axes: %d\n\tNum Buttons: %d\n\tNum Hats: %d\n", SDL_JoystickNumAxes(joy), SDL_JoystickNumButtons(joy), SDL_JoystickNumHats(joy));