Skip to content

Commit

Permalink
Merge pull request #8993 from neilmunday/master
Browse files Browse the repository at this point in the history
Updated SDL2 to use the game controller API
  • Loading branch information
unknownbrackets committed Sep 24, 2016
2 parents 8001f7c + 7d9eed9 commit 246f3aa
Show file tree
Hide file tree
Showing 6 changed files with 299 additions and 242 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Expand Up @@ -1686,6 +1686,7 @@ set(NativeAssets
assets/ppge_atlas.zim
assets/compat.ini
assets/langregion.ini
assets/gamecontrollerdb.txt
assets/unknown.png)
set(LinkCommon ${CoreLibName} ${CMAKE_THREAD_LIBS_INIT} ${nativeExtraLibs})

Expand Down
11 changes: 11 additions & 0 deletions SDL/README.TXT
Expand Up @@ -14,3 +14,14 @@ Open XCode, Preferences, Downloads, Components. Install the command line toools.
Install MacPorts
Using MacPorts, install libpng
Do the above.

SDL2 Game Controller Support Notes
==================================

For SDL2 game controller support, at least SDL 2.0.4 is required.

Under the assets directory is the SDL2 game controller database: gamecontrollerdb.txt. This file contains many known control pad mappings for Windows, Linux and MAC OS. PPSSPPSDL will load this file at start-up and work out how to assign control pad buttons for your control pad.

Hot plugging of control pads is also supported.

If you control pad has a "Guide" or "Home" button then when pressed, this will trigger the emulator pause menu, thus allowing you to exit the emulator if you wish or load another game from your library.
251 changes: 127 additions & 124 deletions SDL/SDLJoystick.cpp
@@ -1,7 +1,11 @@
#include "SDL/SDLJoystick.h"
#include "Core/Config.h"
#include "Common/FileUtil.h"

#include <iostream>
#include <string>

using namespace std;

extern "C" {
int SDLJoystickThreadWrapper(void *SDLJoy){
Expand All @@ -12,175 +16,174 @@ extern "C" {
}

SDLJoystick::SDLJoystick(bool init_SDL ): thread(NULL), running(true) {
if (init_SDL)
{
SDL_setenv("SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS", "1", 0);
SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_VIDEO);
if (init_SDL) {
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER);
}

auto dbPath = File::GetExeDirectory() + "assets/gamecontrollerdb.txt";
cout << "loading control pad mappings from " << dbPath << ": ";

if (SDL_GameControllerAddMappingsFromFile(dbPath.c_str()) == -1) {
cout << "FAILED! Please place gamecontrollerdb.txt in your assets directory." << endl;
} else {
cout << "SUCCESS!" << endl;
setUpControllers();
}
}

void SDLJoystick::setUpControllers() {
int numjoys = SDL_NumJoysticks();
SDL_JoystickEventState(SDL_ENABLE);
for (int i = 0; i < numjoys; i++) {
joys.push_back(SDL_JoystickOpen(i));
// printf("Initialized joystick %d: %s",i,SDL_JoystickNameForIndex(i));
if(strstr(SDL_JoystickNameForIndex(i),"PLAYSTATION(R)3 Controller"))
g_Config.bPS3Controller = true;
setUpController(i);
}
if (controllers.size() > 0) {
cout << "pad 1 has been assigned to control pad: " << SDL_GameControllerName(controllers.front()) << endl;
}
}

if (g_Config.bPS3Controller)
fillMappingPS3();
else
fillMapping();


void SDLJoystick::setUpController(int deviceIndex) {
if (SDL_IsGameController(deviceIndex)) {
SDL_GameController *controller = SDL_GameControllerOpen(deviceIndex);
if (controller) {
if (SDL_GameControllerGetAttached(controller)) {
controllers.push_back(controller);
controllerDeviceMap[SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller))] = deviceIndex;
cout << "found control pad: " << SDL_GameControllerName(controller) << ", loading mapping: ";
auto mapping = SDL_GameControllerMapping(controller);
if (mapping == NULL) {
cout << "FAILED" << endl;
} else {
cout << "SUCCESS, mapping is:" << endl << mapping << endl;
}
} else {
SDL_GameControllerClose(controller);
}
}
}
}

SDLJoystick::~SDLJoystick(){
if (thread)
{
SDLJoystick::~SDLJoystick() {
if (thread) {
running = false;
SDL_Event evt;
evt.type = SDL_USEREVENT;
SDL_PushEvent(&evt);
SDL_WaitThread(thread,0);
}
for (SDL_Joystick *joy : joys)
{
SDL_JoystickClose(joy);
for (auto & controller : controllers) {
SDL_GameControllerClose(controller);
}
}

void SDLJoystick::startEventLoop(){
void SDLJoystick::startEventLoop() {
thread = SDL_CreateThread(SDLJoystickThreadWrapper, "joystick",static_cast<void *>(this));
}

keycode_t SDLJoystick::getKeycodeForButton(SDL_GameControllerButton button) {
switch (button) {
case SDL_CONTROLLER_BUTTON_DPAD_UP:
return NKCODE_DPAD_UP;
case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
return NKCODE_DPAD_DOWN;
case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
return NKCODE_DPAD_LEFT;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
return NKCODE_DPAD_RIGHT;
case SDL_CONTROLLER_BUTTON_A:
return NKCODE_BUTTON_2;
case SDL_CONTROLLER_BUTTON_B:
return NKCODE_BUTTON_3;
case SDL_CONTROLLER_BUTTON_X:
return NKCODE_BUTTON_4;
case SDL_CONTROLLER_BUTTON_Y:
return NKCODE_BUTTON_1;
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
return NKCODE_BUTTON_5;
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
return NKCODE_BUTTON_6;
case SDL_CONTROLLER_BUTTON_START:
return NKCODE_BUTTON_10;
case SDL_CONTROLLER_BUTTON_BACK:
return NKCODE_BUTTON_9; // select button
case SDL_CONTROLLER_BUTTON_GUIDE:
return NKCODE_BACK; // pause menu
}
return NKCODE_UNKNOWN;
}

void SDLJoystick::ProcessInput(SDL_Event &event){
switch (event.type) {
case SDL_JOYAXISMOTION:
{
std::map<int, int>::const_iterator i = SDLJoyAxisMap.find(event.jaxis.axis);
int deviceIndex = getDeviceIndex(event.jaxis.which);
if (i != SDLJoyAxisMap.end() && deviceIndex >= 0) {
AxisInput axis;
axis.axisId = i->second;
// 1.2 to try to approximate the PSP's clamped rectangular range.
axis.value = 1.2 * event.jaxis.value / 32767.0f;
if (axis.value > 1.0f) axis.value = 1.0f;
if (axis.value < -1.0f) axis.value = -1.0f;
axis.deviceId = DEVICE_ID_PAD_0 + deviceIndex;
axis.flags = 0;
NativeAxis(axis);
}
break;
}

case SDL_JOYBUTTONDOWN:
{
std::map<int, int>::const_iterator i = SDLJoyButtonMap.find(event.jbutton.button);
int deviceIndex = getDeviceIndex(event.jbutton.which);
if (i != SDLJoyButtonMap.end() && deviceIndex >= 0) {
case SDL_CONTROLLERBUTTONDOWN:
if (event.cbutton.state == SDL_PRESSED) {
auto code = getKeycodeForButton((SDL_GameControllerButton)event.cbutton.button);
if (code != NKCODE_UNKNOWN) {
KeyInput key;
key.flags = KEY_DOWN;
key.keyCode = i->second;
key.deviceId = DEVICE_ID_PAD_0 + deviceIndex;
key.keyCode = code;
key.deviceId = DEVICE_ID_PAD_0 + getDeviceIndex(event.cbutton.which);
NativeKey(key);
}
break;
}

case SDL_JOYBUTTONUP:
{
std::map<int, int>::const_iterator i = SDLJoyButtonMap.find(event.jbutton.button);
int deviceIndex = getDeviceIndex(event.jbutton.which);
if (i != SDLJoyButtonMap.end() && deviceIndex >= 0) {
break;
case SDL_CONTROLLERBUTTONUP:
if (event.cbutton.state == SDL_RELEASED) {
auto code = getKeycodeForButton((SDL_GameControllerButton)event.cbutton.button);
if (code != NKCODE_UNKNOWN) {
KeyInput key;
key.flags = KEY_UP;
key.keyCode = i->second;
key.deviceId = DEVICE_ID_PAD_0 + deviceIndex;
key.keyCode = code;
key.deviceId = DEVICE_ID_PAD_0 + getDeviceIndex(event.cbutton.which);
NativeKey(key);
}
break;
}

case SDL_JOYHATMOTION:
{
int deviceIndex = getDeviceIndex(event.jhat.which);
if (deviceIndex < 0) {
break;
case SDL_CONTROLLERAXISMOTION:
AxisInput axis;
axis.axisId = event.caxis.axis;
// 1.2 to try to approximate the PSP's clamped rectangular range.
axis.value = 1.2 * event.caxis.value / 32767.0f;
if (axis.value > 1.0f) axis.value = 1.0f;
if (axis.value < -1.0f) axis.value = -1.0f;
axis.deviceId = DEVICE_ID_PAD_0 + getDeviceIndex(event.caxis.which);
axis.flags = 0;
NativeAxis(axis);
break;
case SDL_CONTROLLERDEVICEREMOVED:
// for removal events, "which" is the instance ID for SDL_CONTROLLERDEVICEREMOVED
for (auto it = controllers.begin(); it != controllers.end(); ++it) {
if (SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(*it)) == event.cdevice.which) {
SDL_GameControllerClose(*it);
controllers.erase(it);
break;
}
#ifdef _WIN32
KeyInput key;
key.deviceId = DEVICE_ID_PAD_0 + deviceIndex;

key.flags = (event.jhat.value & SDL_HAT_UP)?KEY_DOWN:KEY_UP;
key.keyCode = NKCODE_DPAD_UP;
NativeKey(key);
key.flags = (event.jhat.value & SDL_HAT_LEFT)?KEY_DOWN:KEY_UP;
key.keyCode = NKCODE_DPAD_LEFT;
NativeKey(key);
key.flags = (event.jhat.value & SDL_HAT_DOWN)?KEY_DOWN:KEY_UP;
key.keyCode = NKCODE_DPAD_DOWN;
NativeKey(key);
key.flags = (event.jhat.value & SDL_HAT_RIGHT)?KEY_DOWN:KEY_UP;
key.keyCode = NKCODE_DPAD_RIGHT;
NativeKey(key);
#else
AxisInput axisX;
AxisInput axisY;
axisX.axisId = JOYSTICK_AXIS_HAT_X;
axisY.axisId = JOYSTICK_AXIS_HAT_Y;
axisX.deviceId = DEVICE_ID_PAD_0 + deviceIndex;
axisY.deviceId = DEVICE_ID_PAD_0 + deviceIndex;
axisX.value = 0.0f;
axisY.value = 0.0f;
if (event.jhat.value & SDL_HAT_LEFT) axisX.value = -1.0f;
if (event.jhat.value & SDL_HAT_RIGHT) axisX.value = 1.0f;
if (event.jhat.value & SDL_HAT_DOWN) axisY.value = 1.0f;
if (event.jhat.value & SDL_HAT_UP) axisY.value = -1.0f;
NativeAxis(axisX);
NativeAxis(axisY);
#endif
break;
}

case SDL_JOYDEVICEADDED:
{
int deviceIndex = event.jdevice.which;
if (deviceIndex >= joys.size()) {
joys.resize(deviceIndex+1);
}
joys[deviceIndex] = SDL_JoystickOpen(deviceIndex);
SDL_JoystickEventState(SDL_ENABLE);
break;
}

case SDL_JOYDEVICEREMOVED:
{
int deviceIndex = getDeviceIndex(event.jdevice.which);
if (deviceIndex >= 0) {
SDL_JoystickClose(joys[deviceIndex]);
joys[deviceIndex] = 0;
}
break;
break;
case SDL_CONTROLLERDEVICEADDED:
// for add events, "which" is the device index!
int prevNumControllers = controllers.size();
setUpController(event.cdevice.which);
if (prevNumControllers == 0 && controllers.size() > 0) {
cout << "pad 1 has been assigned to control pad: " << SDL_GameControllerName(controllers.front()) << endl;
}
break;
}
}

int SDLJoystick::getDeviceIndex(int instanceId) {
for (int i = 0; i < joys.size(); i++) {
SDL_Joystick *joy = joys[i];
if (SDL_JoystickInstanceID(joy) == instanceId) {
return i;
}
auto it = controllerDeviceMap.find(instanceId);
if (it == controllerDeviceMap.end()) {
// could not find device
return -1;
}
return -1;
return it->second;
}

void SDLJoystick::runLoop(){
while (running){
void SDLJoystick::runLoop() {
while (running) {
SDL_Event evt;
int res = SDL_WaitEvent(&evt);
if (res){
if (res) {
ProcessInput(evt);
}
}
Expand Down

0 comments on commit 246f3aa

Please sign in to comment.