Mercurial > repos > blastem
view controller_info.c @ 1971:80920c21bb52
Add an event log soft flush and call it twice per frame in between hard flushes to netplay latency when there are insufficient hardware updates to flush packets in the middle of a frame
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Fri, 08 May 2020 11:40:30 -0700 |
parents | fc05f49075c2 |
children | 8a64d86cc362 |
line wrap: on
line source
#include <string.h> #include <stdlib.h> #ifndef USE_FBDEV #include "render_sdl.h" #endif #include "controller_info.h" #include "config.h" #include "util.h" #include "blastem.h" #include "bindings.h" typedef struct { char const *name; controller_info info; } heuristic; static heuristic heuristics[] = { //TODO: Add more heuristic rules {"DualShock 4", {.type = TYPE_PSX, .subtype = SUBTYPE_PS4}}, {"PS4", {.type = TYPE_PSX, .subtype = SUBTYPE_PS4}}, {"PS3", {.type = TYPE_PSX, .subtype = SUBTYPE_PS3}}, {"X360", {.type = TYPE_XBOX, .subtype = SUBTYPE_X360}}, {"Xbox 360", {.type = TYPE_XBOX, .subtype = SUBTYPE_X360}}, {"X-box 360", {.type = TYPE_XBOX, .subtype = SUBTYPE_X360}}, {"Xbox One", {.type = TYPE_XBOX, .subtype = SUBTYPE_XBONE}}, {"X-box One", {.type = TYPE_XBOX, .subtype = SUBTYPE_XBONE}}, {"WiiU", {.type = TYPE_NINTENDO, .subtype = SUBTYPE_WIIU}}, {"Wii U", {.type = TYPE_NINTENDO, .subtype = SUBTYPE_WIIU}}, {"Nintendo Switch", {.type = TYPE_NINTENDO, .subtype = SUBTYPE_SWITCH}}, {"Saturn", {.type = TYPE_SEGA, .subtype = SUBTYPE_SATURN}} }; const uint32_t num_heuristics = sizeof(heuristics)/sizeof(*heuristics); static tern_node *info_config; static uint8_t loaded; static const char *subtype_names[] = { "unknown", "xbox", "xbox 360", "xbone", "ps2", "ps3", "ps4", "wiiu", "switch", "genesis", "saturn" }; static const char *subtype_human_names[] = { "unknown", "Xbos", "Xbox 360", "Xbox One", "PS2", "PS3", "PS4", "Wii-U", "Switch", "Genesis", "Saturn" }; static const char *variant_names[] = { "normal", "6b bumpers", "6b right" }; static void load_ctype_config(void) { if (!loaded) { info_config = load_overrideable_config("controller_types.cfg", "controller_types.cfg", NULL); loaded = 1; } } controller_info get_controller_info(int joystick) { #ifndef USE_FBDEV load_ctype_config(); char guid_string[33]; SDL_Joystick *stick = render_get_joystick(joystick); SDL_GameController *control = render_get_controller(joystick); SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(stick), guid_string, sizeof(guid_string)); tern_node *info = tern_find_node(info_config, guid_string); if (info) { controller_info res; char *subtype = tern_find_ptr(info, "subtype"); res.subtype = SUBTYPE_UNKNOWN; if (subtype) { for (int i = 0; i < SUBTYPE_NUM; i++) { if (!strcmp(subtype_names[i], subtype)) { res.subtype = i; break; } } } switch (res.subtype) { case SUBTYPE_XBOX: case SUBTYPE_X360: case SUBTYPE_XBONE: res.type = TYPE_XBOX; break; case SUBTYPE_PS2: case SUBTYPE_PS3: case SUBTYPE_PS4: res.type = TYPE_PSX; break; case SUBTYPE_WIIU: case SUBTYPE_SWITCH: res.type = TYPE_NINTENDO; break; case SUBTYPE_GENESIS: case SUBTYPE_SATURN: res.type = TYPE_SEGA; break; default: res.type = TYPE_UNKNOWN; break; } char *variant = tern_find_ptr(info, "variant"); res.variant = VARIANT_NORMAL; if (variant) { for (int i = 0; i < VARIANT_NUM; i++) { if (!strcmp(variant_names[i], variant)) { res.variant = i; break; } } } res.name = control ? SDL_GameControllerName(control) : SDL_JoystickName(stick); if (control) { SDL_GameControllerClose(control); } return res; } if (!control) { return (controller_info) { .type = TYPE_UNKNOWN, .subtype = SUBTYPE_UNKNOWN, .variant = VARIANT_NORMAL, .name = SDL_JoystickName(stick) }; } const char *name = SDL_GameControllerName(control); SDL_GameControllerClose(control); for (uint32_t i = 0; i < num_heuristics; i++) { if (strstr(name, heuristics[i].name)) { controller_info res = heuristics[i].info; res.name = name; return res; } } #else const char *name = "Unknown"; #endif //default to a 360 return (controller_info){ .type = TYPE_GENERIC_MAPPING, .subtype = SUBTYPE_UNKNOWN, .variant = VARIANT_NORMAL, .name = name }; } static void mappings_iter(char *key, tern_val val, uint8_t valtype, void *data) { #ifndef USE_FBDEV if (valtype != TVAL_NODE) { return; } char *mapping = tern_find_ptr(val.ptrval, "mapping"); if (mapping) { const char *parts[] = {key, ",", mapping}; char * full = alloc_concat_m(3, parts); SDL_GameControllerAddMapping(full); free(full); } #endif } void controller_add_mappings(void) { load_ctype_config(); if (info_config) { tern_foreach(info_config, mappings_iter, NULL); } } void save_controller_info(int joystick, controller_info *info) { #ifndef USE_FBDEV char guid_string[33]; SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(render_get_joystick(joystick)), guid_string, sizeof(guid_string)); tern_node *existing = tern_find_node(info_config, guid_string); existing = tern_insert_ptr(existing, "subtype", strdup(subtype_names[info->subtype])); existing = tern_insert_ptr(existing, "variant", strdup(variant_names[info->variant])); info_config = tern_insert_node(info_config, guid_string, existing); persist_config_at(config, info_config, "controller_types.cfg"); handle_joy_added(joystick); #endif } void save_controller_mapping(int joystick, char *mapping_string) { #ifndef USE_FBDEV char guid_string[33]; SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(render_get_joystick(joystick)), guid_string, sizeof(guid_string)); tern_node *existing = tern_find_node(info_config, guid_string); existing = tern_insert_ptr(existing, "mapping", mapping_string); info_config = tern_insert_node(info_config, guid_string, existing); persist_config_at(config, info_config, "controller_types.cfg"); const char *parts[] = {guid_string, ",", mapping_string}; char * full = alloc_concat_m(3, parts); SDL_GameControllerAddMapping(full); free(full); handle_joy_added(joystick); #endif } char const *labels_xbox[] = { "A", "B", "X", "Y", "Back", NULL, "Start", "Click", "Click", "White", "Black", "LT", "RT" }; char const *labels_360[] = { "A", "B", "X", "Y", "Back", "Xbox", "Start", "Click", "Click", "LB", "RB", "LT", "RT" }; static char const *labels_xbone[] = { "A", "B", "X", "Y", "View", "Xbox", "Menu", "Click", "Click", "LB", "RB", "LT", "RT" }; static char const *labels_ps3[] = { "cross", "circle", "square", "triangle", "Select", "PS", "Start", "L3", "R3", "L1", "R1", "L2", "R2" }; static char const *labels_ps4[] = { "cross", "circle", "square", "triangle", "Share", "PS", "Options", "L3", "R3", "L1", "R1", "L2", "R2" }; static char const *labels_nintendo[] = { "B", "A", "Y", "X", "-", "Home", "+", "Click", "Click", "L", "R", "ZL", "ZR" }; static char const *labels_genesis[] = { "A", "B", "X", "Y", NULL, NULL, "Start", NULL, NULL, "Z", "C", NULL, "Mode" }; static char const *labels_saturn[] = { "A", "B", "X", "Y", NULL, NULL, "Start", NULL, NULL, "Z", "C", "LT", "RT" }; static const char** label_source(controller_info *info) { if (info->type == TYPE_UNKNOWN || info->type == TYPE_GENERIC_MAPPING || info->subtype ==SUBTYPE_X360) { return labels_360; } else if (info->type == TYPE_NINTENDO) { return labels_nintendo; } else if (info->type == TYPE_PSX) { if (info->subtype == SUBTYPE_PS4) { return labels_ps4; } else { return labels_ps3; } } else if (info->type == TYPE_XBOX) { if (info->subtype == SUBTYPE_XBONE) { return labels_xbone; } else { return labels_xbox; } } else { if (info->subtype == SUBTYPE_GENESIS) { return labels_genesis; } else { return labels_saturn; } } } const char *get_button_label(controller_info *info, int button) { #ifndef USE_FBDEV if (button >= SDL_CONTROLLER_BUTTON_DPAD_UP) { static char const * dirs[] = {"Up", "Down", "Left", "Right"}; return dirs[button - SDL_CONTROLLER_BUTTON_DPAD_UP]; } #endif return label_source(info)[button]; } static char const *axis_labels[] = { "Left X", "Left Y", "Right X", "Right Y" }; const char *get_axis_label(controller_info *info, int axis) { #ifndef USE_FBDEV if (axis < SDL_CONTROLLER_AXIS_TRIGGERLEFT) { return axis_labels[axis]; } else { return label_source(info)[axis - SDL_CONTROLLER_AXIS_TRIGGERLEFT + SDL_CONTROLLER_BUTTON_RIGHTSHOULDER + 1]; } #else return NULL; #endif } char *make_controller_type_key(controller_info *info) { const char *subtype; if (info->subtype == SUBTYPE_UNKNOWN) { switch(info->type) { case TYPE_XBOX: subtype = subtype_names[SUBTYPE_X360]; break; case TYPE_PSX: subtype = subtype_names[SUBTYPE_PS4]; break; case TYPE_NINTENDO: subtype = subtype_names[SUBTYPE_SWITCH]; break; default: subtype = "unknown"; } } else { subtype = subtype_names[info->subtype]; } const char *variant = variant_names[info->variant]; const char *parts[] = {subtype, "_", variant}; char *ret = alloc_concat_m(3, parts); for (char *cur = ret; *cur; cur++) { if (*cur == ' ') { *cur = '_'; } } return ret; } char *make_human_readable_type_name(controller_info *info) { const char *base = subtype_human_names[info->subtype]; char *prefix; if (info->variant == VARIANT_NORMAL) { prefix = "Normal "; } else { static const char *parts[] = {"6 button (", NULL, "/", NULL, ") "}; #ifdef USE_FBDEV parts[1] = parts[3] = "??"; #else if (info->variant == VARIANT_6B_BUMPERS) { parts[1] = get_button_label(info, SDL_CONTROLLER_BUTTON_LEFTSHOULDER); parts[3] = get_button_label(info, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); } else { parts[1] = get_button_label(info, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER); parts[3] = get_axis_label(info, SDL_CONTROLLER_AXIS_TRIGGERRIGHT); } #endif prefix = alloc_concat_m(5, parts); } char *ret = alloc_concat(prefix, base); if (info->variant != VARIANT_NORMAL) { free(prefix); } return ret; }