Path: blob/master/src/pc/controller/controller_sdl2.c
7861 views
#include <stdio.h>1#include <stdint.h>2#include <stdbool.h>3#include <math.h>45#ifdef TARGET_MACOS6#include <SDL.h>7#else8#include <SDL2/SDL.h>9#endif1011#include <ultra64.h>1213#include "controller_api.h"14#include "controller_sdl.h"1516#include "game/settings.h"1718#define DEADZONE_LEFT gControllerLeftDeadzone * 1019#define DEADZONE_RIGHT gControllerRightDeadzone * 102021// Checks if the button is pressed, and if so adds it to the pressedButtons variable22#define SET_BUTTON(SDL_BUTTON) if (SDL_GameControllerGetButton(sdl_cntrl, SDL_BUTTON)) pressedButtons |= 1 << (SDL_BUTTON + 1)23//Checks if the button is in the pressedButtons variable, and if so passes it to the emulated controls24#define CHECK_BUTTON(CONFIG_BUTTON, GAME_BUTTON) if ((pressedButtons & CONFIG_BUTTON) != 0) pad->button |= GAME_BUTTON2526static bool init_ok;27static SDL_GameController *sdl_cntrl;28static bool haptics_enabled;29static SDL_Haptic *sdl_haptic;30static SDL_Haptic *controller_sdl_init_haptics(const int joy);3132static void controller_sdl_ensure_open(void) {33if (!init_ok) {34return;35}3637SDL_GameControllerUpdate();3839if (sdl_cntrl != NULL && !SDL_GameControllerGetAttached(sdl_cntrl)) {40SDL_HapticClose(sdl_haptic);41SDL_GameControllerClose(sdl_cntrl);42sdl_cntrl = NULL;43sdl_haptic = NULL;44}4546if (sdl_cntrl == NULL) {47for (int i = 0; i < SDL_NumJoysticks(); i++) {48if (SDL_IsGameController(i)) {49sdl_cntrl = SDL_GameControllerOpen(i);50if (sdl_cntrl != NULL) {51sdl_haptic = controller_sdl_init_haptics(i);52break;53}54}55}56}57}5859static u32 controller_sdl_get_raw_buttons(void) {60int16_t ltrig;61int16_t rtrig;62u32 pressedButtons = 0;6364if (!init_ok || sdl_cntrl == NULL) {65return 0;66}6768ltrig = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_TRIGGERLEFT);69rtrig = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_TRIGGERRIGHT);7071SET_BUTTON(SDL_CONTROLLER_BUTTON_START);72SET_BUTTON(SDL_CONTROLLER_BUTTON_BACK);73SET_BUTTON(SDL_CONTROLLER_BUTTON_LEFTSHOULDER);74SET_BUTTON(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);75SET_BUTTON(SDL_CONTROLLER_BUTTON_A);76SET_BUTTON(SDL_CONTROLLER_BUTTON_B);77SET_BUTTON(SDL_CONTROLLER_BUTTON_X);78SET_BUTTON(SDL_CONTROLLER_BUTTON_Y);79SET_BUTTON(SDL_CONTROLLER_BUTTON_LEFTSTICK);80SET_BUTTON(SDL_CONTROLLER_BUTTON_RIGHTSTICK);81SET_BUTTON(SDL_CONTROLLER_BUTTON_DPAD_UP);82SET_BUTTON(SDL_CONTROLLER_BUTTON_DPAD_DOWN);83SET_BUTTON(SDL_CONTROLLER_BUTTON_DPAD_LEFT);84SET_BUTTON(SDL_CONTROLLER_BUTTON_DPAD_RIGHT);85if (ltrig > 30 * 256) pressedButtons |= 1 << 22;86if (rtrig > 30 * 256) pressedButtons |= 1 << 23;8788return pressedButtons;89}9091static void controller_sdl_init(void) {92#if defined(_WIN32) || defined(_WIN64)93if (configBlockNonXinputControllers) {94// Prioritize XInput by disabling other joystick backends.95SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0");96SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI, "0");97}98#endif99100if (SDL_Init(SDL_INIT_GAMECONTROLLER) != 0) {101fprintf(stderr, "SDL init error: %s\n", SDL_GetError());102return;103}104105int added = -1;106char *basePath = SDL_GetBasePath();107if (basePath != NULL) {108char dbPath[4096];109snprintf(dbPath, sizeof(dbPath), "%sgamecontrollerdb.txt", basePath);110added = SDL_GameControllerAddMappingsFromFile(dbPath);111SDL_free(basePath);112}113114// Also try the current working directory just in case115if (added < 0) {116if (SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt") < 0) {117printf("Failed to load gamecontrollerdb.txt: %s\n", SDL_GetError());118}119}120121haptics_enabled = (SDL_InitSubSystem(SDL_INIT_HAPTIC) == 0);122if (!haptics_enabled) {123printf("Haptic init failed (ignoring): %s\n", SDL_GetError());124}125126init_ok = true;127}128129static SDL_Haptic *controller_sdl_init_haptics(const int joy) {130if (!haptics_enabled) return NULL;131132SDL_Haptic *hap = SDL_HapticOpen(joy);133if (hap == NULL) return NULL;134135if (SDL_HapticRumbleSupported(hap) != SDL_TRUE) {136SDL_HapticClose(hap);137return NULL;138}139140if (SDL_HapticRumbleInit(hap) != 0) {141SDL_HapticClose(hap);142return NULL;143}144145printf("controller %s has haptics support, rumble enabled\n", SDL_JoystickNameForIndex(joy));146return hap;147}148149static void controller_sdl_read(OSContPad *pad) {150if (!init_ok) {151return;152}153154controller_sdl_ensure_open();155if (sdl_cntrl == NULL) {156if (sdl_cntrl == NULL) {157return;158}159}160161u32 pressedButtons = controller_sdl_get_raw_buttons();162163CHECK_BUTTON(configButtonA, A_BUTTON);164CHECK_BUTTON(configButtonB, B_BUTTON);165CHECK_BUTTON(configButtonZ, Z_TRIG);166CHECK_BUTTON(configButtonStart, START_BUTTON);167CHECK_BUTTON(configButtonL, L_TRIG);168CHECK_BUTTON(configButtonR, R_TRIG);169CHECK_BUTTON(configButtonCUp, U_CBUTTONS);170CHECK_BUTTON(configButtonCDown, D_CBUTTONS);171CHECK_BUTTON(configButtonCLeft, L_CBUTTONS);172CHECK_BUTTON(configButtonCRight, R_CBUTTONS);173174if (SDL_GameControllerGetButton(sdl_cntrl, SDL_CONTROLLER_BUTTON_DPAD_UP))175pad->button |= U_JPAD;176if (SDL_GameControllerGetButton(sdl_cntrl, SDL_CONTROLLER_BUTTON_DPAD_DOWN))177pad->button |= D_JPAD;178if (SDL_GameControllerGetButton(sdl_cntrl, SDL_CONTROLLER_BUTTON_DPAD_LEFT))179pad->button |= L_JPAD;180if (SDL_GameControllerGetButton(sdl_cntrl, SDL_CONTROLLER_BUTTON_DPAD_RIGHT))181pad->button |= R_JPAD;182183int16_t leftx = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_LEFTX);184int16_t lefty = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_LEFTY);185int16_t rightx = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_RIGHTX);186int16_t righty = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_RIGHTY);187188#ifdef TARGET_WEB189// Firefox has a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1606562190// It sets down y to 32768.0f / 32767.0f, which is greater than the allowed 1.0f,191// which SDL then converts to a int16_t by multiplying by 32767.0f, which overflows into -32768.192// Maximum up will hence never become -32768 with the current version of SDL2,193// so this workaround should be safe in compliant browsers.194if (lefty == -32768) {195lefty = 32767;196}197if (righty == -32768) {198righty = 32767;199}200#endif201202if (configImprovedCamera) {203uint32_t magnitude_sq2 = (uint32_t)(rightx * rightx);204if (magnitude_sq2 > (uint32_t)(DEADZONE_RIGHT * DEADZONE_RIGHT)) {205pad->stick2_x = rightx / 409;206}207}208else {209if (rightx < -0x4000) pad->button |= L_CBUTTONS;210if (rightx > 0x4000) pad->button |= R_CBUTTONS;211}212213if (configVerticalCamera) {214uint32_t magnitude_sq2 = (uint32_t)(righty * righty);215if (magnitude_sq2 > (uint32_t)(DEADZONE_RIGHT * DEADZONE_RIGHT)) {216pad->stick2_y = righty / 409;217}218}219else {220if (righty < -0x4000) pad->button |= U_CBUTTONS;221if (righty > 0x4000) pad->button |= D_CBUTTONS;222}223224//if (ltrig > 30 * 256) pad->button |= configButtonZL;225//if (rtrig > 30 * 256) pad->button |= configButtonZR;226227uint32_t magnitude_sq = (uint32_t)(leftx * leftx) + (uint32_t)(lefty * lefty);228if (magnitude_sq > (uint32_t)(DEADZONE_LEFT * DEADZONE_LEFT)) {229// Game expects stick coordinates within -80..80230// 32768 / 409 = ~80231pad->stick_x = leftx / 409;232pad->stick_y = -lefty / 409;233}234}235236static void controller_sdl_rumble_play(f32 strength, f32 length) {237if (sdl_haptic) {238SDL_HapticRumblePlay(sdl_haptic, strength, (u32)(length * 1000.0f));239}240else {241#if SDL_VERSION_ATLEAST(2, 0, 18)242uint16_t scaled_strength = strength * pow(2, 16) - 1;243if (SDL_GameControllerHasRumble(sdl_cntrl) == SDL_TRUE) {244SDL_GameControllerRumble(sdl_cntrl, scaled_strength, scaled_strength, (u32)(length * 1000.0f));245}246#endif247}248}249250static void controller_sdl_rumble_stop(void) {251if (sdl_haptic) {252SDL_HapticRumbleStop(sdl_haptic);253}254else {255#if SDL_VERSION_ATLEAST(2, 0, 18)256if (SDL_GameControllerHasRumble(sdl_cntrl) == SDL_TRUE) {257SDL_GameControllerRumble(sdl_cntrl, 0, 0, 0);258}259#endif260}261}262263struct ControllerAPI controller_sdl = {264controller_sdl_init,265controller_sdl_read,266controller_sdl_rumble_play,267controller_sdl_rumble_stop268};269270