1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-13 16:44:50 +02:00

Added joystick detection (#174)

This commit is contained in:
vanfanel2
2019-02-26 21:46:49 +01:00
committed by Timur Gagiev
parent e11b5fd895
commit a37d1d5bd6
2 changed files with 93 additions and 456 deletions

425
main.cpp
View File

@@ -1,425 +0,0 @@
#include <string.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <pwd.h>
#include <pthread.h>
#include <alsa/asoundlib.h>
// SDL2 include stuff
#include <SDL2/SDL.h>
#define GL_GLEXT_PROTOTYPES 1
#include <SDL2/SDL_opengles2.h>
//
#include "game.h"
#define WND_TITLE "OpenLara"
// timing
unsigned int startTime;
int osGetTime() {
timeval t;
gettimeofday(&t, NULL);
return int((t.tv_sec - startTime) * 1000 + t.tv_usec / 1000);
}
// sound
snd_pcm_uframes_t SND_FRAMES = 512;
snd_pcm_t *sndOut;
Sound::Frame *sndData;
pthread_t sndThread;
void* sndFill(void *arg) {
while (sndOut) {
Sound::fill(sndData, SND_FRAMES);
int err = snd_pcm_writei(sndOut, sndData, SND_FRAMES);
if (err < 0) {
LOG("! sound: write %s\n", snd_strerror(err));;
if (err != -EPIPE)
break;
err = snd_pcm_recover(sndOut, err, 0);
if (err < 0) {
LOG("! sound: failed to recover\n");
break;
}
snd_pcm_prepare(sndOut);
}
}
return NULL;
}
bool sndInit() {
unsigned int freq = 44100;
int err;
if ((err = snd_pcm_open(&sndOut, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
LOG("! sound: open %s\n", snd_strerror(err));\
sndOut = NULL;
return false;
}
snd_pcm_hw_params_t *params;
snd_pcm_hw_params_alloca(&params);
snd_pcm_hw_params_any(sndOut, params);
snd_pcm_hw_params_set_access(sndOut, params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_channels(sndOut, params, 2);
snd_pcm_hw_params_set_format(sndOut, params, SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_rate_near(sndOut, params, &freq, NULL);
snd_pcm_hw_params_set_periods(sndOut, params, 4, 0);
snd_pcm_hw_params_set_period_size_near(sndOut, params, &SND_FRAMES, NULL);
snd_pcm_hw_params_get_period_size(params, &SND_FRAMES, 0);
snd_pcm_hw_params(sndOut, params);
snd_pcm_prepare(sndOut);
sndData = new Sound::Frame[SND_FRAMES];
memset(sndData, 0, SND_FRAMES * sizeof(Sound::Frame));
if ((err = snd_pcm_writei(sndOut, sndData, SND_FRAMES)) < 0) {
LOG("! sound: write %s\n", snd_strerror(err));\
sndOut = NULL;
}
snd_pcm_start(sndOut);
pthread_create(&sndThread, NULL, sndFill, NULL);
return true;
}
void sndFree() {
pthread_cancel(sndThread);
snd_pcm_drop(sndOut);
snd_pcm_drain(sndOut);
snd_pcm_close(sndOut);
delete[] sndData;
}
// Input
#define MAX_JOYS 4
#define JOY_DEAD_ZONE_STICK 8192
struct sdl_input *sdl_inputs;
int sdl_numjoysticks, sdl_numcontrollers;
SDL_Joystick *sdl_joysticks[MAX_JOYS];
SDL_GameController *sdl_controllers[MAX_JOYS];
vec2 joyL, joyR;
bool osJoyReady(int index) {
return index == 0; // TODO
}
void osJoyVibrate(int index, float L, float R) {
// TODO
}
InputKey codeToInputKey(int code) {
switch (code) {
// keyboard
case SDL_SCANCODE_LEFT : return ikLeft;
case SDL_SCANCODE_RIGHT : return ikRight;
case SDL_SCANCODE_UP : return ikUp;
case SDL_SCANCODE_DOWN : return ikDown;
case SDL_SCANCODE_SPACE : return ikSpace;
case SDL_SCANCODE_TAB : return ikTab;
case SDL_SCANCODE_RETURN : return ikEnter;
case SDL_SCANCODE_ESCAPE : return ikEscape;
case SDL_SCANCODE_LSHIFT :
case SDL_SCANCODE_RSHIFT : return ikShift;
case SDL_SCANCODE_LCTRL :
case SDL_SCANCODE_RCTRL : return ikCtrl;
case SDL_SCANCODE_LALT :
case SDL_SCANCODE_RALT : return ikAlt;
case SDL_SCANCODE_0 : return ik0;
case SDL_SCANCODE_1 : return ik1;
case SDL_SCANCODE_2 : return ik2;
case SDL_SCANCODE_3 : return ik3;
case SDL_SCANCODE_4 : return ik4;
case SDL_SCANCODE_5 : return ik5;
case SDL_SCANCODE_6 : return ik6;
case SDL_SCANCODE_7 : return ik7;
case SDL_SCANCODE_8 : return ik8;
case SDL_SCANCODE_9 : return ik9;
case SDL_SCANCODE_A : return ikA;
case SDL_SCANCODE_B : return ikB;
case SDL_SCANCODE_C : return ikC;
case SDL_SCANCODE_D : return ikD;
case SDL_SCANCODE_E : return ikE;
case SDL_SCANCODE_F : return ikF;
case SDL_SCANCODE_G : return ikG;
case SDL_SCANCODE_H : return ikH;
case SDL_SCANCODE_I : return ikI;
case SDL_SCANCODE_J : return ikJ;
case SDL_SCANCODE_K : return ikK;
case SDL_SCANCODE_L : return ikL;
case SDL_SCANCODE_M : return ikM;
case SDL_SCANCODE_N : return ikN;
case SDL_SCANCODE_O : return ikO;
case SDL_SCANCODE_P : return ikP;
case SDL_SCANCODE_Q : return ikQ;
case SDL_SCANCODE_R : return ikR;
case SDL_SCANCODE_S : return ikS;
case SDL_SCANCODE_T : return ikT;
case SDL_SCANCODE_U : return ikU;
case SDL_SCANCODE_V : return ikV;
case SDL_SCANCODE_W : return ikW;
case SDL_SCANCODE_X : return ikX;
case SDL_SCANCODE_Y : return ikY;
case SDL_SCANCODE_Z : return ikZ;
case SDL_SCANCODE_AC_HOME : return ikEscape;
}
return ikNone;
}
JoyKey controllerCodeToJoyKey(int code) {
// joystick using the modern SDL GameController interface
switch (code) {
case SDL_CONTROLLER_BUTTON_A : return jkA;
case SDL_CONTROLLER_BUTTON_B : return jkB;
case SDL_CONTROLLER_BUTTON_X : return jkX;
case SDL_CONTROLLER_BUTTON_Y : return jkY;
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER : return jkLB;
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER : return jkRB;
case SDL_CONTROLLER_BUTTON_BACK : return jkSelect;
case SDL_CONTROLLER_BUTTON_START : return jkStart;
case SDL_CONTROLLER_BUTTON_LEFTSTICK : return jkL;
case SDL_CONTROLLER_BUTTON_RIGHTSTICK : return jkR;
}
return jkNone;
}
JoyKey joyCodeToJoyKey(int buttonNumber) {
// joystick using the classic SDL Joystick interface
switch (buttonNumber) {
case 0 : return jkY;
case 1 : return jkB;
case 2 : return jkA;
case 3 : return jkX;
case 4 : return jkL;
case 5 : return jkR;
case 6 : return jkLB;
case 7 : return jkRB;
case 8 : return jkSelect;
case 9 : return jkStart;
}
return jkNone;
}
bool inputInit() {
int i;
joyL = joyR = vec2(0);
sdl_numjoysticks = SDL_NumJoysticks();
sdl_numjoysticks = (sdl_numjoysticks < MAX_JOYS )? sdl_numjoysticks : MAX_JOYS;
for (i = 0; i < sdl_numjoysticks; i++) {
if(SDL_IsGameController(i)) {
SDL_GameController *controller = SDL_GameControllerOpen(i);
sdl_joysticks[i] = SDL_GameControllerGetJoystick(controller);
sdl_controllers[i] = controller;
sdl_numcontrollers++;
}
else {
sdl_joysticks[i] = SDL_JoystickOpen(i);
}
}
return true;
}
void inputFree() {
int i;
for (i = 0; i < sdl_numcontrollers; i++) {
SDL_GameControllerClose(sdl_controllers[i]);
}
for (i = 0; i < sdl_numjoysticks; i++) {
SDL_JoystickClose(sdl_joysticks[i]);
}
}
float joyAxisValue(int value) {
if (value > -JOY_DEAD_ZONE_STICK && value < JOY_DEAD_ZONE_STICK)
return 0.0f;
return value / 32767.0f;
}
float joyTrigger(int value) {
return min(1.0f, value / 255.0f);
}
vec2 joyDir(const vec2 &value) {
float dist = min(1.0f, value.length());
return value.normal() * dist;
}
int getJoyIndex(SDL_JoystickID id) {
int i;
for (i=0 ; i < sdl_numjoysticks; i++) {
if (SDL_JoystickInstanceID(sdl_joysticks[i]) == id) {
return i;
}
}
return 0;
}
void inputUpdate() {
// get input events
int joyIndex;
SDL_Event event;
while (SDL_PollEvent(&event) == 1) { // while there are still events to be processed
switch (event.type) {
case SDL_KEYDOWN: {
int scancode = event.key.keysym.scancode;
InputKey key = codeToInputKey(scancode);
if (key != ikNone) {
Input::setDown(key, 1);
}
break;
}
case SDL_KEYUP: {
int scancode = event.key.keysym.scancode;
InputKey key = codeToInputKey(scancode);
if (key != ikNone) {
Input::setDown(key, 0);
}
break;
}
// Joystick reading using the modern GameController interface
case SDL_CONTROLLERBUTTONDOWN: {
joyIndex = getJoyIndex(event.cbutton.which);
JoyKey key = controllerCodeToJoyKey(event.cbutton.button);
Input::setJoyDown(joyIndex, key, 1);
break;
}
case SDL_CONTROLLERBUTTONUP: {
joyIndex = getJoyIndex(event.cbutton.which);
JoyKey key = controllerCodeToJoyKey(event.cbutton.button);
Input::setJoyDown(joyIndex, key, 0);
break;
}
case SDL_CONTROLLERAXISMOTION: {
joyIndex = getJoyIndex(event.caxis.which);
switch (event.caxis.axis) {
case SDL_CONTROLLER_AXIS_LEFTX: joyL.x = joyAxisValue(event.caxis.value); break;
case SDL_CONTROLLER_AXIS_LEFTY: joyL.y = joyAxisValue(event.caxis.value); break;
case SDL_CONTROLLER_AXIS_RIGHTX: joyR.x = joyAxisValue(event.caxis.value); break;
case SDL_CONTROLLER_AXIS_RIGHTY: joyR.y = joyAxisValue(event.caxis.value); break;
}
Input::setJoyPos(joyIndex, jkL, joyDir(joyL));
Input::setJoyPos(joyIndex, jkR, joyDir(joyR));
break;
}
// Joystick reading using the classic Joystick interface
case SDL_JOYBUTTONDOWN: {
joyIndex = getJoyIndex(event.jbutton.which);
JoyKey key = joyCodeToJoyKey(event.jbutton.button);
Input::setJoyDown(joyIndex, key, 1);
break;
}
case SDL_JOYBUTTONUP: {
joyIndex = getJoyIndex(event.jbutton.which);
JoyKey key = joyCodeToJoyKey(event.jbutton.button);
Input::setJoyDown(joyIndex, key, 0);
break;
}
case SDL_JOYAXISMOTION: {
joyIndex = getJoyIndex(event.jaxis.which);
switch (event.jaxis.axis) {
// In the classic joystick interface we know what axis changed by it's number,
// they have no names like on the fancy GameController interface.
case 0: joyL.x = joyAxisValue(event.jaxis.value); break;
case 1: joyL.y = joyAxisValue(event.jaxis.value); break;
case 2: joyR.x = joyAxisValue(event.jaxis.value); break;
case 3: joyR.y = joyAxisValue(event.jaxis.value); break;
}
Input::setJoyPos(joyIndex, jkL, joyDir(joyL));
Input::setJoyPos(joyIndex, jkR, joyDir(joyR));
break;
}
}
}
}
int main(int argc, char **argv) {
int w, h;
SDL_DisplayMode current;
SDL_Init(SDL_INIT_VIDEO|SDL_INIT_EVENTS|SDL_INIT_GAMECONTROLLER);
SDL_GetCurrentDisplayMode(0, &current);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
// We start in fullscreen mode using the vide mode currently in use, to avoid video mode changes.
SDL_Window *window = SDL_CreateWindow(WND_TITLE, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
//current.w, current.h, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_FULLSCREEN_DESKTOP);
640, 480, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
// We try to use the current video mode, but we inform the core of whatever mode SDL2 gave us in the end.
SDL_GetWindowSize(window, &w, &h);
Core::width = w;
Core::height = h;
SDL_GLContext context = SDL_GL_CreateContext(window);
SDL_GL_SetSwapInterval(0);
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1,
SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
SDL_ShowCursor(SDL_DISABLE);
cacheDir[0] = saveDir[0] = contentDir[0] = 0;
const char *home;
if (!(home = getenv("HOME")))
home = getpwuid(getuid())->pw_dir;
strcat(cacheDir, home);
strcat(cacheDir, "/.openlara/");
struct stat st = {0};
if (stat(cacheDir, &st) == -1 && mkdir(cacheDir, 0777) == -1)
cacheDir[0] = 0;
strcpy(saveDir, cacheDir);
timeval t;
gettimeofday(&t, NULL);
startTime = t.tv_sec;
sndInit();
inputInit();
char *lvlName = argc > 1 ? argv[1] : NULL;
Game::init(lvlName);
while (!Core::isQuit) {
inputUpdate();
if (Game::update()) {
Game::render();
Core::waitVBlank();
SDL_GL_SwapWindow(window);
}
};
sndFree();
Game::deinit();
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}

View File

@@ -217,35 +217,94 @@ JoyKey joyCodeToJoyKey(int buttonNumber) {
return jkNone;
}
bool inputInit() {
int joyGetIndex(SDL_JoystickID id) {
int i;
for (i=0 ; i < sdl_numjoysticks; i++) {
if (SDL_JoystickInstanceID(sdl_joysticks[i]) == id) {
return i;
}
}
return -1;
}
// To know if it's a gamecontroller when we only have the instanceID
bool joyIsController (Sint32 instanceID) {
int i;
bool ret = false;
// We can't use SDL_IsGameController after we have physically disconnected a joystick, so we use this workaround.
for (i = 0; i < sdl_numcontrollers; i++) {
if (SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(sdl_controllers[i])) == instanceID) {
ret = true;
break;
}
}
return ret;
}
void joyAdd(int index) {
if(SDL_IsGameController(index)) {
SDL_GameController *controller = SDL_GameControllerOpen(index);
sdl_controllers[index] = controller;
sdl_joysticks[index] = SDL_GameControllerGetJoystick(controller);
sdl_numcontrollers++;
}
else {
sdl_joysticks[index] = SDL_JoystickOpen(index);
}
// Update number of joysticks
sdl_numjoysticks = SDL_NumJoysticks();
sdl_numjoysticks = (sdl_numjoysticks < MAX_JOYS )? sdl_numjoysticks : MAX_JOYS;
}
void joyRemove(Sint32 instanceID) {
int i;
// Closing game controller
if (joyIsController(instanceID)) {
for (i = 0; i < sdl_numcontrollers; i++) {
if (SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(sdl_controllers[i])) == instanceID) {
SDL_GameControllerClose(sdl_controllers[i]);
sdl_controllers[i] = NULL;
sdl_numcontrollers--;
sdl_numjoysticks--;
}
}
}
// Closing joystick
else {
i = joyGetIndex(instanceID);
if (i >= 0) {
SDL_JoystickClose(sdl_joysticks[i]);
sdl_numjoysticks--;
}
}
}
bool inputInit() {
int index;
joyL = joyR = vec2(0);
sdl_numjoysticks = SDL_NumJoysticks();
sdl_numjoysticks = (sdl_numjoysticks < MAX_JOYS )? sdl_numjoysticks : MAX_JOYS;
l_alt = false;
k_enter = false;
for (i = 0; i < sdl_numjoysticks; i++) {
if(SDL_IsGameController(i)) {
SDL_GameController *controller = SDL_GameControllerOpen(i);
sdl_joysticks[i] = SDL_GameControllerGetJoystick(controller);
sdl_controllers[i] = controller;
sdl_numcontrollers++;
}
else {
sdl_joysticks[i] = SDL_JoystickOpen(i);
}
for (index = 0; index < MAX_JOYS; index++)
sdl_joysticks[index] = NULL;
for (index = 0; index < sdl_numjoysticks; index++) {
joyAdd(index);
}
return true;
}
void inputFree() {
int i;
for (i = 0; i < sdl_numcontrollers; i++) {
SDL_GameControllerClose(sdl_controllers[i]);
}
Sint32 instanceID;
for (i = 0; i < sdl_numjoysticks; i++) {
SDL_JoystickClose(sdl_joysticks[i]);
instanceID = SDL_JoystickInstanceID(sdl_joysticks[i]);
joyRemove(instanceID);
}
}
@@ -264,16 +323,6 @@ vec2 joyDir(const vec2 &value) {
return value.normal() * dist;
}
int getJoyIndex(SDL_JoystickID id) {
int i;
for (i=0 ; i < sdl_numjoysticks; i++) {
if (SDL_JoystickInstanceID(sdl_joysticks[i]) == id) {
return i;
}
}
return 0;
}
void inputUpdate() {
// get input events
@@ -300,19 +349,19 @@ void inputUpdate() {
}
// Joystick reading using the modern GameController interface
case SDL_CONTROLLERBUTTONDOWN: {
joyIndex = getJoyIndex(event.cbutton.which);
joyIndex = joyGetIndex(event.cbutton.which);
JoyKey key = controllerCodeToJoyKey(event.cbutton.button);
Input::setJoyDown(joyIndex, key, 1);
break;
}
case SDL_CONTROLLERBUTTONUP: {
joyIndex = getJoyIndex(event.cbutton.which);
joyIndex = joyGetIndex(event.cbutton.which);
JoyKey key = controllerCodeToJoyKey(event.cbutton.button);
Input::setJoyDown(joyIndex, key, 0);
break;
}
case SDL_CONTROLLERAXISMOTION: {
joyIndex = getJoyIndex(event.caxis.which);
joyIndex = joyGetIndex(event.caxis.which);
switch (event.caxis.axis) {
case SDL_CONTROLLER_AXIS_LEFTX: joyL.x = joyAxisValue(event.caxis.value); break;
case SDL_CONTROLLER_AXIS_LEFTY: joyL.y = joyAxisValue(event.caxis.value); break;
@@ -325,19 +374,19 @@ void inputUpdate() {
}
// Joystick reading using the classic Joystick interface
case SDL_JOYBUTTONDOWN: {
joyIndex = getJoyIndex(event.jbutton.which);
joyIndex = joyGetIndex(event.jbutton.which);
JoyKey key = joyCodeToJoyKey(event.jbutton.button);
Input::setJoyDown(joyIndex, key, 1);
break;
}
case SDL_JOYBUTTONUP: {
joyIndex = getJoyIndex(event.jbutton.which);
joyIndex = joyGetIndex(event.jbutton.which);
JoyKey key = joyCodeToJoyKey(event.jbutton.button);
Input::setJoyDown(joyIndex, key, 0);
break;
}
case SDL_JOYAXISMOTION: {
joyIndex = getJoyIndex(event.jaxis.which);
joyIndex = joyGetIndex(event.jaxis.which);
switch (event.jaxis.axis) {
// In the classic joystick interface we know what axis changed by it's number,
// they have no names like on the fancy GameController interface.
@@ -350,6 +399,19 @@ void inputUpdate() {
Input::setJoyPos(joyIndex, jkR, joyDir(joyR));
break;
}
// Joystick connection or disconnection
case SDL_JOYDEVICEADDED : {
// Upon connection, 'which' is the internal SDL2 joystick index,
// but on disconnection, 'which' is the instanceID.
// We store the joysticks in their corresponding position on the joysticks array,
// IE: joystick with index 3 will be in sdl_joysticks[3].
joyAdd(event.jdevice.which);
break;
}
case SDL_JOYDEVICEREMOVED : {
joyRemove(event.jdevice.which);
break;
}
}
}
}