Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated SDL2 to use the game controller API #8993

Merged
merged 5 commits into from
Sep 24, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xsacha will this work on Qt? I think it bundles the assets into the executable?

-[Unknown]

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
Loading