# HG changeset patch # User Michael Pavone # Date 1485131039 28800 # Node ID 6a4503fad67e2e3655dfeeb4859d1d13a617a277 # Parent 110251ea369eeaeb9c66e3247dfce7a7f30a60d5 Initial support for using SDL2 game controller mapping functionality diff -r 110251ea369e -r 6a4503fad67e default.cfg --- a/default.cfg Sun Jan 22 16:13:02 2017 -0800 +++ b/default.cfg Sun Jan 22 16:23:59 2017 -0800 @@ -42,14 +42,15 @@ } } 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 @@ } } 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 -r 110251ea369e -r 6a4503fad67e io.c --- a/io.c Sun Jan 22 16:13:02 2017 -0800 +++ b/io.c Sun Jan 22 16:23:59 2017 -0800 @@ -923,6 +923,49 @@ } } +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 @@ 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 @@ } 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 -r 110251ea369e -r 6a4503fad67e render.h --- a/render.h Sun Jan 22 16:13:02 2017 -0800 +++ b/render.h Sun Jan 22 16:23:59 2017 -0800 @@ -63,6 +63,12 @@ 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_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 -r 110251ea369e -r 6a4503fad67e render_sdl.c --- a/render_sdl.c Sun Jan 22 16:13:02 2017 -0800 +++ b/render_sdl.c Sun Jan 22 16:23:59 2017 -0800 @@ -111,6 +111,7 @@ } static SDL_Joystick * joysticks[MAX_JOYSTICKS]; +static int joystick_sdl_index[MAX_JOYSTICKS]; int render_width() { @@ -271,7 +272,7 @@ 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 @@ 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 @@ 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));