Game::Game(){ SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER); SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1"); this->mainWindow = SDL_CreateWindow("Highway Crossing Frog", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, this->width, this->height, SDL_WINDOW_SHOWN); this->mainRenderer = SDL_CreateRenderer(this->mainWindow, -1, SDL_RENDERER_ACCELERATED); if(SDL_GameControllerAddMapping(GASIA_PS2_MAP) == 1){ printf("Managed to add silly PS2 adapter thing.\n"); } if(SDL_GameControllerAddMapping(GASIA_PS2_LIN) == 1){ printf("Managed to add silly PS2 adapter thing on Linux.\n"); } if(SDL_NumJoysticks() > 0){ for (int i = 0; i < SDL_NumJoysticks(); i++) { if(SDL_IsGameController(i)){ this->gamePad = SDL_GameControllerOpen(i); if(this->gamePad){ printf("Opened controller %i\n", i); } else { printf("Failed to open controller %i\n", i); } } } } else { printf("No controllers found.\n"); } this->gWorld = new GameWorld(this->mainRenderer); this->running = true; }
void InputDaemon::refreshMapping(QString mapping, InputDevice *device) { bool found = false; for (int i=0; i < SDL_NumJoysticks() && !found; i++) { SDL_Joystick *joystick = SDL_JoystickOpen(i); SDL_JoystickID joystickID = SDL_JoystickInstanceID(joystick); if (device->getSDLJoystickID() == joystickID) { found = true; if (SDL_IsGameController(i)) { // Mapping string updated. Perform basic refresh QByteArray tempbarray = mapping.toUtf8(); SDL_GameControllerAddMapping(tempbarray.data()); } else { // Previously registered as a plain joystick. Add // mapping and check for validity. If SDL accepts it, // close current device and re-open as // a game controller. SDL_GameControllerAddMapping(mapping.toUtf8().constData()); if (SDL_IsGameController(i)) { device->closeSDLDevice(); trackjoysticks.remove(joystickID); joysticks->remove(joystickID); SDL_GameController *controller = SDL_GameControllerOpen(i); GameController *damncontroller = new GameController(controller, i, settings, this); connect(damncontroller, SIGNAL(requestWait()), eventWorker, SLOT(haltServices())); SDL_Joystick *sdlStick = SDL_GameControllerGetJoystick(controller); joystickID = SDL_JoystickInstanceID(sdlStick); joysticks->insert(joystickID, damncontroller); trackcontrollers.insert(joystickID, damncontroller); emit deviceUpdated(i, damncontroller); } } } // Make sure to decrement reference count SDL_JoystickClose(joystick); } }
static void SDL_GameControllerLoadHints() { const char *hint = SDL_GetHint(SDL_HINT_GAMECONTROLLERCONFIG); if ( hint && hint[0] ) { int nchHints = SDL_strlen( hint ); char *pUserMappings = SDL_malloc( nchHints + 1 ); char *pTempMappings = pUserMappings; SDL_memcpy( pUserMappings, hint, nchHints ); while ( pUserMappings ) { char *pchNewLine = NULL; pchNewLine = SDL_strchr( pUserMappings, '\n' ); if ( pchNewLine ) *pchNewLine = '\0'; SDL_GameControllerAddMapping( pUserMappings ); if ( pchNewLine ) pUserMappings = pchNewLine + 1; else pUserMappings = NULL; } SDL_free(pTempMappings); } }
/* * Initialize the game controller system, mostly load our DB of controller config mappings */ int SDL_GameControllerInit(void) { int i = 0; const char *pMappingString = NULL; s_pSupportedControllers = NULL; pMappingString = s_ControllerMappings[i]; while (pMappingString) { SDL_GameControllerAddMapping(pMappingString); i++; pMappingString = s_ControllerMappings[i]; } /* load in any user supplied config */ SDL_GameControllerLoadHints(); /* watch for joy events and fire controller ones if needed */ SDL_AddEventWatch(SDL_GameControllerEventWatcher, NULL); /* Send added events for controllers currently attached */ for (i = 0; i < SDL_NumJoysticks(); ++i) { if (SDL_IsGameController(i)) { SDL_Event deviceevent; deviceevent.type = SDL_CONTROLLERDEVICEADDED; deviceevent.cdevice.which = i; SDL_PushEvent(&deviceevent); } } return (0); }
void DEV_Joystick::Init() { #ifdef WITH_SDL if (!(SDL_CHECK(SDL_InitSubSystem)) || !(SDL_CHECK(SDL_GameControllerAddMapping))) { return; } /* Initializing Game Controller related subsystems */ bool success = (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) != -1 ); if (success) { /* Loading Game Controller mapping data base from a string */ unsigned short i = 0; const char *mapping_string = NULL; mapping_string = controller_mappings[i]; while (mapping_string) { SDL_GameControllerAddMapping(mapping_string); i++; mapping_string = controller_mappings[i]; } } else { CM_Error("initializing SDL Game Controller: " << SDL_GetError()); } #endif }
inline bool add_controller_mapping(const std::string& mapping) { int result = SDL_GameControllerAddMapping(mapping.c_str()); if(result == -1) { GUM_ERROR_HANDLER(false); } return result == 1; }
int GameController::setMapping(State & state, SDL_GameController * gamecontroller){ Stack * stack = state.stack; if (stack->is<LUA_TSTRING>(1)){ const std::string mapping = stack->to<const std::string>(1); stack->push<bool>(SDL_GameControllerAddMapping(mapping.c_str())); } return 0; }
int Java_org_libsdl_app_SDLActivity_nativeInit(JNIEnv* env, jclass cls, jobject obj) { setenv("OPENMW_DECOMPRESS_TEXTURES", "1", 1); // On Android, we use a virtual controller with guid="Virtual" SDL_GameControllerAddMapping("5669727475616c000000000000000000,Virtual,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4"); return 0; }
/* * SDL.gameControllerAddMapping(name) * * Arguments: * name the mapping string * * Returns: * 1, 0 or -1 * The error message */ static int l_gameControllerAddMapping(lua_State *L) { const char *mapping = luaL_checkstring(L, 1); int ret; ret = SDL_GameControllerAddMapping(mapping); if (ret < 0) return commonPushSDLError(L, 1); return commonPush(L, "i", ret); }
void OSystem_PSP2::initBackend() { ConfMan.set("joystick_num", 0); ConfMan.registerDefault("fullscreen", true); ConfMan.registerDefault("aspect_ratio", false); ConfMan.registerDefault("gfx_mode", "2x"); ConfMan.registerDefault("filtering", true); ConfMan.registerDefault("kbdmouse_speed", 3); ConfMan.registerDefault("joystick_deadzone", 2); ConfMan.registerDefault("shader", 0); ConfMan.registerDefault("touchpad_mouse_mode", false); ConfMan.registerDefault("frontpanel_touchpad_mode", false); if (!ConfMan.hasKey("fullscreen")) { ConfMan.setBool("fullscreen", true); } if (!ConfMan.hasKey("aspect_ratio")) { ConfMan.setBool("aspect_ratio", false); } if (!ConfMan.hasKey("gfx_mode")) { ConfMan.set("gfx_mode", "2x"); } if (!ConfMan.hasKey("filtering")) { ConfMan.setBool("filtering", true); } if (!ConfMan.hasKey("shader")) { ConfMan.setInt("shader", 2); } if (!ConfMan.hasKey("touchpad_mouse_mode")) { ConfMan.setBool("touchpad_mouse_mode", false); } if (!ConfMan.hasKey("frontpanel_touchpad_mode")) { ConfMan.setBool("frontpanel_touchpad_mode", false); } // Create the savefile manager if (_savefileManager == 0) _savefileManager = new DefaultSaveFileManager("ux0:data/scummvm/saves"); // Controller mappings for Vita, various names have been used in various SDL versions SDL_GameControllerAddMapping("50535669746120436f6e74726f6c6c65,PSVita Controller,y:b0,b:b1,a:b2,x:b3,leftshoulder:b4,rightshoulder:b5,dpdown:b6,dpleft:b7,dpup:b8,dpright:b9,back:b10,start:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,"); SDL_GameControllerAddMapping("50535669746120636f6e74726f6c6c65,PSVita controller,y:b0,b:b1,a:b2,x:b3,leftshoulder:b4,rightshoulder:b5,dpdown:b6,dpleft:b7,dpup:b8,dpright:b9,back:b10,start:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,"); SDL_GameControllerAddMapping("50535669746120636f6e74726f6c6c65,PSVita controller 2,y:b0,b:b1,a:b2,x:b3,leftshoulder:b4,rightshoulder:b5,dpdown:b6,dpleft:b7,dpup:b8,dpright:b9,back:b10,start:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,"); SDL_GameControllerAddMapping("50535669746120636f6e74726f6c6c65,PSVita controller 3,y:b0,b:b1,a:b2,x:b3,leftshoulder:b4,rightshoulder:b5,dpdown:b6,dpleft:b7,dpup:b8,dpright:b9,back:b10,start:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,"); SDL_GameControllerAddMapping("50535669746120636f6e74726f6c6c65,PSVita controller 4,y:b0,b:b1,a:b2,x:b3,leftshoulder:b4,rightshoulder:b5,dpdown:b6,dpleft:b7,dpup:b8,dpright:b9,back:b10,start:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,"); SDL_GameControllerAddMapping("505356697461206275696c74696e206a,PSVita builtin joypad,y:b0,b:b1,a:b2,x:b3,leftshoulder:b4,rightshoulder:b5,dpdown:b6,dpleft:b7,dpup:b8,dpright:b9,back:b10,start:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,"); // Event source if (_eventSource == 0) _eventSource = new PSP2EventSource(); // Graphics Manager if (_graphicsManager == 0) _graphicsManager = new PSP2SdlGraphicsManager(_eventSource, _window); // Invoke parent implementation of this method OSystem_SDL::initBackend(); }
static int _SaveMappings() { char bind[2056]; const char* name; if (_this->controller != NULL) { name = SDL_GameControllerName(_this->controller); } else { name = SDL_JoystickName(_this->joystick); } SDL_JoystickGUID guid = SDL_JoystickGetGUID(_this->joystick); char guid_string[33]; SDL_JoystickGetGUIDString(guid, guid_string, 33); guid_string[32] = 0; snprintf(bind, 2056, "%s,%s,platform:%s,", guid_string, name, SDL_GetPlatform()); int index = strlen(bind); for (int i = 0; i < NUM_MAPPINGS; ++i) { const char* to = NULL; if (_this->mappings[i].type == SDL_CONTROLLER_BINDTYPE_AXIS) { to = SDL_GameControllerGetStringForAxis(_this->mappings[i].value.axis); } else if (_this->mappings[i].type != SDL_CONTROLLER_BINDTYPE_BUTTON){ to = SDL_GameControllerGetStringForButton(_this->mappings[i].value.button); } if (to == NULL) { continue; } char from[10]; if (_this->mappings[i].bind.bindType == SDL_CONTROLLER_BINDTYPE_AXIS) { snprintf(from, 10, "a%d", _this->mappings[i].bind.value.axis); } else if (_this->mappings[i].bind.bindType == SDL_CONTROLLER_BINDTYPE_BUTTON) { snprintf(from, 10, "b%d", _this->mappings[i].bind.value.button); } if (_this->mappings[i].bind.bindType == SDL_CONTROLLER_BINDTYPE_HAT) { snprintf(from, 10, "h%d.%d", _this->mappings[i].bind.value.hat.hat, _this->mappings[i].bind.value.hat.hat_mask); } else { continue; } snprintf(bind, 2056 - index, "%s:%s,", to, from); index += strlen(to) + strlen(from); } SDL_GameControllerAddMapping(bind); // TODO Save to file SDL_Log("Mapping: %s\n", bind); }
void SDLEventReader::initSDL() { #ifdef USE_SDL_2 // SDL_INIT_GAMECONTROLLER should automatically initialize SDL_INIT_JOYSTICK // but it doesn't seem to be the case with v2.0.4 SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_JOYSTICK); #else // Video support is required to use event system in SDL 1.2. SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK); #endif SDL_JoystickEventState(SDL_ENABLE); sdlIsOpen = true; #ifdef USE_SDL_2 //QSettings settings(PadderCommon::configFilePath, QSettings::IniFormat); settings->getLock()->lock(); settings->beginGroup("Mappings"); QStringList mappings = settings->allKeys(); QStringListIterator iter(mappings); while (iter.hasNext()) { QString tempstring = iter.next(); QString mappingSetting = settings->value(tempstring, QString()).toString(); if (!mappingSetting.isEmpty()) { QByteArray temparray = mappingSetting.toUtf8(); char *mapping = temparray.data(); SDL_GameControllerAddMapping(mapping); // Let SDL take care of validation } } settings->endGroup(); settings->getLock()->unlock(); //SDL_GameControllerAddMapping("03000000100800000100000010010000,Twin USB Joystick,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftshoulder:b6,rightshoulder:b7,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b4,righttrigger:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2"); #endif pollRateTimer.stop(); pollRateTimer.setInterval(pollRate); //pollRateTimer.start(); //pollRateTimer.setSingleShot(true); emit sdlStarted(); }
/* * Add controller mapping from string */ void S2D_AddControllerMapping(const char *map) { int result = SDL_GameControllerAddMapping(map); char guid[33]; strncpy(guid, map, 32); switch (result) { case 1: S2D_Log(S2D_INFO, "Mapping added for GUID: %s", guid); break; case 0: S2D_Log(S2D_INFO, "Mapping updated for GUID: %s", guid); break; case -1: S2D_Error("SDL_GameControllerAddMapping", SDL_GetError()); break; } }
/* * Initialize the game controller system, mostly load our DB of controller config mappings */ int SDL_GameControllerInit(void) { int i = 0; const char *pMappingString = NULL; s_pSupportedControllers = NULL; pMappingString = s_ControllerMappings[i]; while ( pMappingString ) { SDL_GameControllerAddMapping( pMappingString ); i++; pMappingString = s_ControllerMappings[i]; } /* load in any user supplied config */ SDL_GameControllerLoadHints(); /* watch for joy events and fire controller ones if needed */ SDL_AddEventWatch( SDL_GameControllerEventWatcher, NULL ); return (0); }
/* * Add or update an entry into the Mappings Database */ int SDL_GameControllerAddMappingsFromRW(SDL_RWops * rw, int freerw) { const char *platform = SDL_GetPlatform(); int controllers = 0; char *buf, *line, *line_end, *tmp, *comma, line_platform[64]; size_t db_size, platform_len; if (rw == NULL) { return SDL_SetError("Invalid RWops"); } db_size = (size_t)SDL_RWsize(rw); buf = (char *)SDL_malloc(db_size + 1); if (buf == NULL) { if (freerw) { SDL_RWclose(rw); } return SDL_SetError("Could allocate space to not read DB into memory"); } if (SDL_RWread(rw, buf, db_size, 1) != 1) { if (freerw) { SDL_RWclose(rw); } SDL_free(buf); return SDL_SetError("Could not read DB"); } if (freerw) { SDL_RWclose(rw); } buf[db_size] = '\0'; line = buf; while (line < buf + db_size) { line_end = SDL_strchr(line, '\n'); if (line_end != NULL) { *line_end = '\0'; } else { line_end = buf + db_size; } /* Extract and verify the platform */ tmp = SDL_strstr(line, SDL_CONTROLLER_PLATFORM_FIELD); if (tmp != NULL) { tmp += SDL_strlen(SDL_CONTROLLER_PLATFORM_FIELD); comma = SDL_strchr(tmp, ','); if (comma != NULL) { platform_len = comma - tmp + 1; if (platform_len + 1 < SDL_arraysize(line_platform)) { SDL_strlcpy(line_platform, tmp, platform_len); if (SDL_strncasecmp(line_platform, platform, platform_len) == 0 && SDL_GameControllerAddMapping(line) > 0) { controllers++; } } } } line = line_end + 1; } SDL_free(buf); return controllers; }
void Remapper::dataIn( Node::DataType type, QMutex *mutex, void *data, size_t bytes, qint64 timeStamp ) { switch( type ) { case DataType::Input: { // Copy incoming data to our own buffer GamepadState gamepad; { mutex->lock(); gamepad = *reinterpret_cast<GamepadState *>( data ); mutex->unlock(); } int instanceID = gamepad.instanceID; int joystickID = gamepad.joystickID; QString GUID( QByteArray( reinterpret_cast<const char *>( gamepad.GUID.data ), 16 ).toHex() ); // Do initial calibration if not done yet { if( deadzoneFlag[ GUID ] ) { deadzoneFlag[ GUID ] = false; // Apply default value for( int i = 0; i < gamepad.joystickNumAxes; i++ ) { deadzones[ GUID ][ i ] = 10000; // Check analog value at this moment. If its magnitude is less than 30000 then it's most likely // an analog stick. Otherwise, it might be a trigger (with a centered value of -32768) deadzoneModes[ GUID ][ i ] = ( qAbs( static_cast<int>( gamepad.joystickAxis[ i ] ) ) < 30000 ); } // TODO: Replace with stored value from disk } } // Inject deadzone settings into gamepad { for( int i = 0; i < gamepad.joystickNumAxes; i++ ) { gamepad.deadzone[ i ] = deadzones[ GUID ][ i ]; gamepad.deadzoneMode[ i ] = deadzoneModes[ GUID ][ i ]; } } // Send raw joystick data to the model // Do this before we apply joystick deadzones so the user sees completely unprocessed data { // Copy current gamepad into buffer this->mutex.lock(); gamepadBuffer[ gamepadBufferIndex ] = gamepad; this->mutex.unlock(); // Send buffer on its way emit rawJoystickData( &( this->mutex ), reinterpret_cast<void *>( &gamepadBuffer[ gamepadBufferIndex ] ) ); // Increment the index gamepadBufferIndex = ( gamepadBufferIndex + 1 ) % 100; } // Apply deadzones to each stick and both triggers independently { qreal deadzone = 0.0; bool deadzoneMode = false; for( int i = 0; i < 4; i++ ) { int xAxis; int yAxis; // For the analog sticks, average the underlying joystick axes together to get the final deadzone value // If either axis has deadzone mode set to true, it'll apply to both // FIXME: If users complain about this, expand the code to handle this case (one axis true and one axis false) and treat axes indepenently switch( i ) { case 0: { xAxis = SDL_CONTROLLER_AXIS_LEFTX; yAxis = SDL_CONTROLLER_AXIS_LEFTY; Val val = gameControllerToJoystick[ GUID ][ Key( AXIS, SDL_CONTROLLER_AXIS_LEFTX ) ]; int axisID = val.second.first; if( val.first == AXIS ) { deadzone = deadzones[ GUID ][ axisID ]; deadzoneMode = deadzoneModes[ GUID ][ axisID ]; } Val val2 = gameControllerToJoystick[ GUID ][ Key( AXIS, SDL_CONTROLLER_AXIS_LEFTY ) ]; axisID = val2.second.first; if( val2.first == AXIS ) { deadzone += deadzones[ GUID ][ axisID ]; deadzone /= 2.0; deadzoneMode = deadzoneMode || deadzoneModes[ GUID ][ axisID ]; } break; } case 1: { xAxis = SDL_CONTROLLER_AXIS_RIGHTX; yAxis = SDL_CONTROLLER_AXIS_RIGHTY; Val val = gameControllerToJoystick[ GUID ][ Key( AXIS, SDL_CONTROLLER_AXIS_RIGHTX ) ]; int axisID = val.second.first; if( val.first == AXIS ) { deadzone = deadzones[ GUID ][ axisID ]; deadzoneMode = deadzoneModes[ GUID ][ axisID ]; } Val val2 = gameControllerToJoystick[ GUID ][ Key( AXIS, SDL_CONTROLLER_AXIS_RIGHTY ) ]; axisID = val2.second.first; if( val2.first == AXIS ) { deadzone += deadzones[ GUID ][ axisID ]; deadzone /= 2.0; deadzoneMode = deadzoneMode || deadzoneModes[ GUID ][ axisID ]; } break; } // For simplicity, just map the triggers to the line y = x case 2: { xAxis = SDL_CONTROLLER_AXIS_TRIGGERLEFT; yAxis = SDL_CONTROLLER_AXIS_TRIGGERLEFT; Val val = gameControllerToJoystick[ GUID ][ Key( AXIS, SDL_CONTROLLER_AXIS_TRIGGERLEFT ) ]; int axisID = val.second.first; if( val.first == AXIS ) { deadzone = deadzones[ GUID ][ axisID ]; deadzoneMode = deadzoneModes[ GUID ][ axisID ]; } break; } case 3: { xAxis = SDL_CONTROLLER_AXIS_TRIGGERRIGHT; yAxis = SDL_CONTROLLER_AXIS_TRIGGERRIGHT; Val val = gameControllerToJoystick[ GUID ][ Key( AXIS, SDL_CONTROLLER_AXIS_TRIGGERRIGHT ) ]; int axisID = val.second.first; if( val.first == AXIS ) { deadzone = deadzones[ GUID ][ axisID ]; deadzoneMode = deadzoneModes[ GUID ][ axisID ]; } break; } } // Map from [-32768, 32767] to [0, 32767] if( !deadzoneMode ) { gamepad.axis[ xAxis ] /= 2; gamepad.axis[ yAxis ] /= 2; gamepad.axis[ xAxis ] += 16384; gamepad.axis[ yAxis ] += 16384; } // Get axis coords in cartesian coords // Bottom right is positive -> top right is positive qreal xCoord = gamepad.axis[ xAxis ]; qreal yCoord = -gamepad.axis[ yAxis ]; // Get radius from center QVector2D position( static_cast<float>( xCoord ), static_cast<float>( yCoord ) ); qreal radius = static_cast<qreal>( position.length() ); if( !( radius > deadzone ) ) { gamepad.axis[ xAxis ] = 0; gamepad.axis[ yAxis ] = 0; if( xAxis == SDL_CONTROLLER_AXIS_TRIGGERLEFT ) { gamepad.digitalL2 = false; } if( xAxis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT ) { gamepad.digitalR2 = false; } } else { if( xAxis == SDL_CONTROLLER_AXIS_TRIGGERLEFT ) { gamepad.digitalL2 = true; } if( xAxis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT ) { gamepad.digitalR2 = true; } } } } // Apply deadzones to all joystick axes // Used only when detecting input for remapping { for( int i = 0; i < 16; i++ ) { qreal deadzoneRadius = deadzones[ GUID ][ i ]; qreal coord = gamepad.joystickAxis[ i ]; if( !deadzoneModes[ GUID ][ i ] ) { coord += 32768; } if( !( qAbs( coord ) > deadzoneRadius ) ) { gamepad.joystickAxis[ i ] = 0; } } } // If ignoreMode is set, the user hasn't let go of the button they were remapping to // Do not let the button go through until they let go { if( ignoreMode && ignoreModeGUID == GUID && ignoreModeInstanceID == gamepad.instanceID ) { if( ignoreModeVal.first == BUTTON ) { if( gamepad.joystickButton[ ignoreModeVal.second.first ] == SDL_PRESSED ) { gamepad.joystickButton[ ignoreModeVal.second.first ] = SDL_RELEASED; } else { ignoreMode = false; } } else if( ignoreModeVal.first == HAT ) { if( gamepad.joystickHat[ ignoreModeVal.second.first ] != SDL_HAT_CENTERED ) { gamepad.joystickHat[ ignoreModeVal.second.first ] = SDL_HAT_CENTERED; } else { ignoreMode = false; } } else if( ignoreModeVal.first == AXIS ) { if( gamepad.joystickAxis[ ignoreModeVal.second.first ] != 0 ) { gamepad.joystickAxis[ ignoreModeVal.second.first ] = 0; } else { ignoreMode = false; } } } } // If we opened an SDL2 Game controller handle from this class, keep it and inject it into all gamepads we send out { if( gameControllerHandles[ instanceID ] ) { gamepad.gamecontrollerHandle = gameControllerHandles[ instanceID ]; } } // If we are in remap mode, check for a button press from the stored GUID, and remap the stored button to that button // All game controller states are cleared past this point if in remap mode { if( remapMode && GUID == remapModeGUID ) { // Find a button press, the first one we encounter will be the new remapping bool foundInput = false; Key key = remapModeKey; Val value = Val( INVALID, VHat( -1, -1 ) ); // Prioritize buttons and hats over analog sticks for( int joystickButton = 0; joystickButton < 256; joystickButton++ ) { if( gamepad.joystickButton[ joystickButton ] == SDL_PRESSED ) { qCDebug( phxInput ).nospace() << "Button b" << joystickButton << " from GUID " << GUID << " now activates " << keyToMappingString( key ); foundInput = true; value.first = BUTTON; value.second = VHat( joystickButton, -1 ); break; } } for( int joystickHat = 0; joystickHat < 16; joystickHat++ ) { if( gamepad.joystickHat[ joystickHat ] != SDL_HAT_CENTERED ) { qCDebug( phxInput ).nospace() << "Hat h" << joystickHat << "." << gamepad.joystickHat[ joystickHat ] << " from GUID " << GUID << " now activates " << keyToMappingString( key ); foundInput = true; value.first = HAT; value.second = VHat( joystickHat, gamepad.joystickHat[ joystickHat ] ); break; } } for( int joystickAxis = 0; joystickAxis < 16; joystickAxis++ ) { if( gamepad.joystickAxis[ joystickAxis ] != 0 ) { qCDebug( phxInput ).nospace() << "Axis a" << joystickAxis << " from GUID " << GUID << " now activates " << keyToMappingString( key ); foundInput = true; value.first = AXIS; value.second = VHat( joystickAxis, -1 ); break; } } if( foundInput ) { // Store the new remapping internally gameControllerToJoystick[ GUID ][ remapModeKey ] = value; // Tell SDL2 about it QString mappingString; QString platform( SDL_GetPlatform() ); { QString friendlyName = QString( SDL_JoystickName( gamepad.joystickHandle ) ); mappingString.append( GUID ).append( "," ).append( friendlyName ).append( "," ); for( Key key : gameControllerToJoystick[ GUID ].keys() ) { if( gameControllerToJoystick[ GUID ][ key ].first != INVALID ) { mappingString.append( keyToMappingString( key ) ).append( ':' ) .append( valToMappingString( gameControllerToJoystick[ GUID ][ key ] ) ).append( ',' ); } } mappingString.append( "platform:" ).append( platform ).append( "," ); qDebug().nospace() << mappingString; // Give SDL the new mapping string SDL_GameControllerAddMapping( mappingString.toUtf8().constData() ); // If this is not a game controller, reopen as one SDL_GameController *gamecontrollerHandle = nullptr; if( !gameControllerHandles[ instanceID ] ) { gamecontrollerHandle = SDL_GameControllerOpen( joystickID ); gamepad.gamecontrollerHandle = gamecontrollerHandle; // Store internally so we can inject it into all future events from this instanceID gameControllerHandles[ instanceID ] = gamecontrollerHandle; qDebug() << "Opened newly remapped joystick as a game controller:" << gamecontrollerHandle; } } // Store this mapping to disk { if( !userDataPath.isEmpty() ) { QFile mappingFile( userDataPath + "/gamecontrollerdb.txt" ); mappingFile.open( QIODevice::ReadWrite | QIODevice::Text ); QByteArray mappingFileData = mappingFile.readAll(); if( !mappingFile.isOpen() ) { qWarning() << "Unable to open mapping file for reading" << mappingFile.errorString(); } mappingFile.close(); QTextStream mappingFileStreamIn( &mappingFileData ); mappingFileStreamIn.setCodec( "UTF-8" ); mappingFile.open( QIODevice::WriteOnly | QIODevice::Text ); if( !mappingFile.isOpen() ) { qWarning() << "Unable to open mapping file for writing" << mappingFile.errorString(); } QTextStream mappingFileStreamOut( &mappingFile ); mappingFileStreamOut.setCodec( "UTF-8" ); QString line = ""; while( !line.isNull() ) { line = mappingFileStreamIn.readLine(); // We want to replace the line (any line) for our platform that contains our GUID // We'll also filter out empty lines if( line.isEmpty() || ( line.contains( GUID ) && line.contains( platform ) ) ) { continue; } mappingFileStreamOut << line << endl; } mappingFileStreamOut << mappingString << endl; mappingFile.close(); } else { qWarning() << "Unable to open controller mapping file, user data path not set"; } } // End remap mode, start ignore mode { remapMode = false; ignoreMode = true; ignoreModeGUID = GUID; ignoreModeVal = value; ignoreModeInstanceID = gamepad.instanceID; } // Tell the model we're done { emit setMapping( GUID, keyToMappingString( key ), valToFriendlyString( value ) ); emit remappingEnded(); } } // Clear all game controller states (joystick states are untouched) for( int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++ ) { gamepad.button[ i ] = 0; } for( int i = 0; i < SDL_CONTROLLER_AXIS_MAX; i++ ) { gamepad.axis[ i ] = 0; } } else if( remapMode ) { // Clear all gamepad states for( int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++ ) { gamepad.button[ i ] = 0; } for( int i = 0; i < SDL_CONTROLLER_AXIS_MAX; i++ ) { gamepad.axis[ i ] = 0; } } } // OR all joystick button, hat and analog states together by GUID for RemapperModel to indicate presses { for( int i = 0; i < 256; i++ ) { pressed[ GUID ] |= gamepad.joystickButton[ i ]; } for( int i = 0; i < 16; i++ ) { pressed[ GUID ] |= ( gamepad.joystickHat[ i ] != SDL_HAT_CENTERED ); } for( int i = 0; i < 16; i++ ) { pressed[ GUID ] |= ( gamepad.joystickAxis[ i ] != 0 ); } } // Apply axis to d-pad, if enabled // This will always be enabled if we're not currently playing so GlobalGamepad can use the analog stick { if( analogToDpad[ GUID ] || !playing ) { // TODO: Support other axes? int xAxis = SDL_CONTROLLER_AXIS_LEFTX; int yAxis = SDL_CONTROLLER_AXIS_LEFTY; // TODO: Let user configure these qreal threshold = 16384.0; // Size in degrees of the arc covering and centered around each cardinal direction // If <90, there will be gaps in the diagonals // If >180, this code will always produce diagonal inputs qreal rangeDegrees = 180.0 - 45.0; // Get axis coords in cartesian coords // Bottom right is positive -> top right is positive qreal xCoord = gamepad.axis[ xAxis ]; qreal yCoord = -gamepad.axis[ yAxis ]; // Get radius from center QVector2D position( static_cast<float>( xCoord ), static_cast<float>( yCoord ) ); qreal radius = static_cast<qreal>( position.length() ); // Get angle in degrees qreal angle = qRadiansToDegrees( qAtan2( yCoord, xCoord ) ); if( angle < 0.0 ) { angle += 360.0; } if( radius > threshold ) { qreal halfRange = rangeDegrees / 2.0; if( angle > 90.0 - halfRange && angle < 90.0 + halfRange ) { gamepad.button[ SDL_CONTROLLER_BUTTON_DPAD_UP ] = true; } if( angle > 270.0 - halfRange && angle < 270.0 + halfRange ) { gamepad.button[ SDL_CONTROLLER_BUTTON_DPAD_DOWN ] = true; } if( angle > 180.0 - halfRange && angle < 180.0 + halfRange ) { gamepad.button[ SDL_CONTROLLER_BUTTON_DPAD_LEFT ] = true; } if( angle > 360.0 - halfRange || angle < 0.0 + halfRange ) { gamepad.button[ SDL_CONTROLLER_BUTTON_DPAD_RIGHT ] = true; } } } } // Apply d-pad to axis, if enabled { if( dpadToAnalog[ GUID ] ) { gamepad = mapDpadToAnalog( gamepad ); } } // Send updated data out { // Copy current gamepad into buffer this->mutex.lock(); gamepadBuffer[ gamepadBufferIndex ] = gamepad; this->mutex.unlock(); // Send buffer on its way emit dataOut( DataType::Input, &( this->mutex ), reinterpret_cast<void *>( &gamepadBuffer[ gamepadBufferIndex ] ), 0, nodeCurrentTime() ); // Increment the index gamepadBufferIndex = ( gamepadBufferIndex + 1 ) % 100; } break; } case DataType::KeyboardInput: { // Unpack keyboard states and write to gamepad according to remap data { mutex->lock(); KeyboardState keyboard = *reinterpret_cast<KeyboardState *>( data ); for( int i = keyboard.head; i < keyboard.tail; i = ( i + 1 ) % 128 ) { int key = keyboard.key[ i ]; bool pressed = keyboard.pressed[ i ]; if( keyboardKeyToSDLButton.contains( key ) ) { keyboardGamepad.button[ keyboardKeyToSDLButton[ key ] ] = pressed ? SDL_PRESSED : SDL_RELEASED; } } mutex->unlock(); } // OR all key states together and store that value for( int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++ ) { keyboardKeyPressed |= keyboardGamepad.button[ i ]; } // Apply d-pad to axis, if enabled if( dpadToAnalogKeyboard ) { keyboardGamepad = mapDpadToAnalog( keyboardGamepad, true ); } // Send gamepad on its way { // Copy current gamepad into buffer this->mutex.lock(); gamepadBuffer[ gamepadBufferIndex ] = keyboardGamepad; this->mutex.unlock(); // Send buffer on its way emit dataOut( DataType::Input, &( this->mutex ), reinterpret_cast< void * >( &gamepadBuffer[ gamepadBufferIndex ] ), 0, nodeCurrentTime() ); // Increment the index gamepadBufferIndex = ( gamepadBufferIndex + 1 ) % 100; } break; } default: { emit dataOut( type, mutex, data, bytes, timeStamp ); break; } } }
void Gamepad::AddMapping (const char* content) { SDL_GameControllerAddMapping (content); }