CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/SDL/SDLJoystick.cpp
Views: 1401
#include <iostream>1#include <string>23#include "Common/File/FileUtil.h"4#include "Common/File/VFS/VFS.h"5#include "Common/StringUtils.h"6#include "Common/System/NativeApp.h"7#include "Common/System/System.h"89#include "Core/Config.h"10#include "Core/KeyMap.h"11#include "SDL/SDLJoystick.h"1213using namespace std;1415static int SDLJoystickEventHandlerWrapper(void* userdata, SDL_Event* event)16{17static_cast<SDLJoystick *>(userdata)->ProcessInput(*event);18return 0;19}2021SDLJoystick::SDLJoystick(bool init_SDL ) : registeredAsEventHandler(false) {22SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");23if (init_SDL) {24SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER);25}2627const char *dbPath = "gamecontrollerdb.txt";28cout << "loading control pad mappings from " << dbPath << ": ";2930size_t size;31u8 *mappingData = g_VFS.ReadFile(dbPath, &size);32if (mappingData) {33SDL_RWops *rw = SDL_RWFromConstMem(mappingData, size);34// 1 to free the rw after use35if (SDL_GameControllerAddMappingsFromRW(rw, 1) == -1) {36cout << "Failed to read mapping data - corrupt?" << endl;37}38delete[] mappingData;39} else {40cout << "gamecontrollerdb.txt missing" << endl;41}42cout << "SUCCESS!" << endl;43setUpControllers();44}4546void SDLJoystick::setUpControllers() {47int numjoys = SDL_NumJoysticks();48for (int i = 0; i < numjoys; i++) {49setUpController(i);50}51if (controllers.size() > 0) {52cout << "pad 1 has been assigned to control pad: " << SDL_GameControllerName(controllers.front()) << endl;53}54}5556void SDLJoystick::setUpController(int deviceIndex) {57static constexpr int cbGUID = 33;58char pszGUID[cbGUID];5960if (!SDL_IsGameController(deviceIndex)) {61cout << "Control pad device " << deviceIndex << " not supported by SDL game controller database, attempting to create default mapping..." << endl;62SDL_Joystick *joystick = SDL_JoystickOpen(deviceIndex);63if (joystick) {64SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), pszGUID, cbGUID);65// create default mapping - this is the PS3 dual shock mapping66const std::string safeName = ReplaceAll(SDL_JoystickName(joystick), ",", "");67std::string mapping = std::string(pszGUID) + "," + safeName + ",x:b3,a:b0,b:b1,y:b2,back:b8,guide:b10,start:b9,dpleft:b15,dpdown:b14,dpright:b16,dpup:b13,leftshoulder:b4,lefttrigger:a2,rightshoulder:b6,rightshoulder:b5,righttrigger:a5,leftstick:b7,leftstick:b11,rightstick:b12,leftx:a0,lefty:a1,rightx:a3,righty:a4";68if (SDL_GameControllerAddMapping(mapping.c_str()) == 1){69cout << "Added default mapping ok" << endl;70} else {71cout << "Failed to add default mapping" << endl;72}73SDL_JoystickClose(joystick);74} else {75cout << "Failed to get joystick identifier. Read-only device? Control pad device " + std::to_string(deviceIndex) << endl;76}77} else {78SDL_Joystick *joystick = SDL_JoystickOpen(deviceIndex);79if (joystick) {80SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), pszGUID, cbGUID);81SDL_JoystickClose(joystick);82}83}84SDL_GameController *controller = SDL_GameControllerOpen(deviceIndex);85if (controller) {86if (SDL_GameControllerGetAttached(controller)) {87controllers.push_back(controller);88controllerDeviceMap[SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller))] = deviceIndex;89cout << "found control pad: " << SDL_GameControllerName(controller) << ", loading mapping: ";90// NOTE: The case to InputDeviceID here is wrong, we should do some kind of lookup.91KeyMap::NotifyPadConnected((InputDeviceID)deviceIndex, std::string(pszGUID) + ": " + SDL_GameControllerName(controller));92auto mapping = SDL_GameControllerMapping(controller);93if (mapping == NULL) {94//cout << "FAILED" << endl;95cout << "Could not find mapping in SDL2 controller database" << endl;96} else {97cout << "SUCCESS, mapping is:" << endl << mapping << endl;98}99} else {100SDL_GameControllerClose(controller);101}102}103}104105SDLJoystick::~SDLJoystick() {106if (registeredAsEventHandler) {107SDL_DelEventWatch(SDLJoystickEventHandlerWrapper, this);108}109for (auto & controller : controllers) {110SDL_GameControllerClose(controller);111}112}113114void SDLJoystick::registerEventHandler() {115SDL_AddEventWatch(SDLJoystickEventHandlerWrapper, this);116registeredAsEventHandler = true;117}118119InputKeyCode SDLJoystick::getKeycodeForButton(SDL_GameControllerButton button) {120switch (button) {121case SDL_CONTROLLER_BUTTON_DPAD_UP:122return NKCODE_DPAD_UP;123case SDL_CONTROLLER_BUTTON_DPAD_DOWN:124return NKCODE_DPAD_DOWN;125case SDL_CONTROLLER_BUTTON_DPAD_LEFT:126return NKCODE_DPAD_LEFT;127case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:128return NKCODE_DPAD_RIGHT;129case SDL_CONTROLLER_BUTTON_A:130return NKCODE_BUTTON_2;131case SDL_CONTROLLER_BUTTON_B:132return NKCODE_BUTTON_3;133case SDL_CONTROLLER_BUTTON_X:134return NKCODE_BUTTON_4;135case SDL_CONTROLLER_BUTTON_Y:136return NKCODE_BUTTON_1;137case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:138return NKCODE_BUTTON_5;139case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:140return NKCODE_BUTTON_6;141case SDL_CONTROLLER_BUTTON_START:142return NKCODE_BUTTON_10;143case SDL_CONTROLLER_BUTTON_BACK:144return NKCODE_BUTTON_9; // select button145case SDL_CONTROLLER_BUTTON_GUIDE:146return NKCODE_BACK; // pause menu147case SDL_CONTROLLER_BUTTON_LEFTSTICK:148return NKCODE_BUTTON_THUMBL;149case SDL_CONTROLLER_BUTTON_RIGHTSTICK:150return NKCODE_BUTTON_THUMBR;151152// Found these limits by checking out the SDL2 branch of the SDL repo, doing git blame, then `git tag --contains (commit)` etc.153#if SDL_VERSION_ATLEAST(2, 0, 16)154case SDL_CONTROLLER_BUTTON_MISC1:155return NKCODE_BUTTON_11;156#endif157#if SDL_VERSION_ATLEAST(2, 0, 28)158case SDL_CONTROLLER_BUTTON_PADDLE1:159return NKCODE_BUTTON_12;160case SDL_CONTROLLER_BUTTON_PADDLE2:161return NKCODE_BUTTON_13;162case SDL_CONTROLLER_BUTTON_PADDLE3:163return NKCODE_BUTTON_14;164case SDL_CONTROLLER_BUTTON_PADDLE4:165return NKCODE_BUTTON_15;166#endif167#if SDL_VERSION_ATLEAST(2, 0, 14)168case SDL_CONTROLLER_BUTTON_TOUCHPAD:169return NKCODE_BUTTON_16;170#endif171case SDL_CONTROLLER_BUTTON_INVALID:172default:173return NKCODE_UNKNOWN;174}175}176177void SDLJoystick::ProcessInput(const SDL_Event &event){178switch (event.type) {179case SDL_CONTROLLERBUTTONDOWN:180if (event.cbutton.state == SDL_PRESSED) {181auto code = getKeycodeForButton((SDL_GameControllerButton)event.cbutton.button);182if (code != NKCODE_UNKNOWN) {183KeyInput key;184key.flags = KEY_DOWN;185key.keyCode = code;186key.deviceId = DEVICE_ID_PAD_0 + getDeviceIndex(event.cbutton.which);187NativeKey(key);188}189}190break;191case SDL_CONTROLLERBUTTONUP:192if (event.cbutton.state == SDL_RELEASED) {193auto code = getKeycodeForButton((SDL_GameControllerButton)event.cbutton.button);194if (code != NKCODE_UNKNOWN) {195KeyInput key;196key.flags = KEY_UP;197key.keyCode = code;198key.deviceId = DEVICE_ID_PAD_0 + getDeviceIndex(event.cbutton.which);199NativeKey(key);200}201}202break;203case SDL_CONTROLLERAXISMOTION:204{205InputDeviceID deviceId = DEVICE_ID_PAD_0 + getDeviceIndex(event.caxis.which);206// TODO: Can we really cast axis IDs like that? Do they match?207InputAxis axisId = (InputAxis)event.caxis.axis;208float value = event.caxis.value * (1.f / 32767.f);209if (value > 1.0f) value = 1.0f;210if (value < -1.0f) value = -1.0f;211// Filter duplicate axis values.212auto key = std::pair<InputDeviceID, InputAxis>(deviceId, axisId);213auto iter = prevAxisValue_.find(key);214if (iter == prevAxisValue_.end()) {215prevAxisValue_[key] = value;216} else if (iter->second != value) {217iter->second = value;218AxisInput axis;219axis.axisId = axisId;220axis.value = value;221axis.deviceId = deviceId;222NativeAxis(&axis, 1);223} // else ignore event.224break;225}226case SDL_CONTROLLERDEVICEREMOVED:227// for removal events, "which" is the instance ID for SDL_CONTROLLERDEVICEREMOVED228for (auto it = controllers.begin(); it != controllers.end(); ++it) {229if (SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(*it)) == event.cdevice.which) {230SDL_GameControllerClose(*it);231controllers.erase(it);232break;233}234}235break;236case SDL_CONTROLLERDEVICEADDED:237// for add events, "which" is the device index!238int prevNumControllers = controllers.size();239setUpController(event.cdevice.which);240if (prevNumControllers == 0 && controllers.size() > 0) {241cout << "pad 1 has been assigned to control pad: " << SDL_GameControllerName(controllers.front()) << endl;242}243break;244}245}246247int SDLJoystick::getDeviceIndex(int instanceId) {248auto it = controllerDeviceMap.find(instanceId);249if (it == controllerDeviceMap.end()) {250// could not find device251return -1;252}253return it->second;254}255256257