1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-09 06:36:59 +02:00

SDL: rewrote joystick handling and add multiple joystick support. (#170)

* SDL: rewrote joystick handling and add multiple joystick support.

* SDL: Move SDL2 platform audio system to SDL2 functions

* SDL: Add fullscreen toggle via ALT+ENTER. Rearrange SDL2 GL/GLES header includes.
This commit is contained in:
vanfanel2
2019-02-26 03:07:36 +01:00
committed by Timur Gagiev
parent 57882de04b
commit ba45fe0d4e
4 changed files with 650 additions and 166 deletions

425
main.cpp Normal file
View File

@@ -0,0 +1,425 @@
#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

@@ -29,7 +29,7 @@
extern void osToggleVR(bool enable); extern void osToggleVR(bool enable);
#elif __SDL2__ #elif __SDL2__
#define _GAPI_GL 1 #define _GAPI_GL 1
#ifdef SDL2GLES #ifdef SDL2_GLES
#define _GAPI_GLES 1 #define _GAPI_GLES 1
#define DYNGEOM_NO_VBO #define DYNGEOM_NO_VBO
#endif #endif

View File

@@ -19,10 +19,11 @@
#elif defined(__SDL2__) #elif defined(__SDL2__)
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#if !defined(_GAPI_GLES)
#include <SDL2/SDL_opengl.h> #if defined(_GAPI_GLES)
#else #define GL_GLEXT_PROTOTYPES 1
#include <SDL2/SDL_opengles2.h> #include <SDL2/SDL_opengles2.h>
#include <SDL2/SDL_opengles2_gl2ext.h>
#define GL_CLAMP_TO_BORDER 0x812D #define GL_CLAMP_TO_BORDER 0x812D
#define GL_TEXTURE_BORDER_COLOR 0x1004 #define GL_TEXTURE_BORDER_COLOR 0x1004
@@ -45,7 +46,6 @@
#define GL_RG16F GL_RGBA #define GL_RG16F GL_RGBA
#define GL_HALF_FLOAT GL_HALF_FLOAT_OES #define GL_HALF_FLOAT GL_HALF_FLOAT_OES
#define GL_TEXTURE_3D 0
#define GL_TEXTURE_WRAP_R 0 #define GL_TEXTURE_WRAP_R 0
#define GL_DEPTH_STENCIL GL_DEPTH_STENCIL_OES #define GL_DEPTH_STENCIL GL_DEPTH_STENCIL_OES
#define GL_UNSIGNED_INT_24_8 GL_UNSIGNED_INT_24_8_OES #define GL_UNSIGNED_INT_24_8 GL_UNSIGNED_INT_24_8_OES
@@ -59,6 +59,10 @@
#define GL_PROGRAM_BINARY_LENGTH GL_PROGRAM_BINARY_LENGTH_OES #define GL_PROGRAM_BINARY_LENGTH GL_PROGRAM_BINARY_LENGTH_OES
#define glGetProgramBinary(...) #define glGetProgramBinary(...)
#define glProgramBinary(...) #define glProgramBinary(...)
#else
#define GL_GLEXT_PROTOTYPES 1
#include <SDL2/SDL_opengl.h>
#include <SDL2/SDL_opengl_glext.h>
#endif #endif
#elif defined(_OS_RPI) || defined(_OS_CLOVER) #elif defined(_OS_RPI) || defined(_OS_CLOVER)
@@ -501,7 +505,7 @@ namespace GAPI {
sprintf(defines + strlen(defines), "#define %s\n", DefineName[def[i]]); sprintf(defines + strlen(defines), "#define %s\n", DefineName[def[i]]);
} }
#if defined(_OS_RPI) || defined(_OS_CLOVER) #if defined(_OS_RPI) || defined(_OS_CLOVER) || (defined (__SDL2__) && defined (_GAPI_GLES))
strcat(defines, "#define OPT_VLIGHTPROJ\n"); strcat(defines, "#define OPT_VLIGHTPROJ\n");
strcat(defines, "#define OPT_VLIGHTVEC\n"); strcat(defines, "#define OPT_VLIGHTVEC\n");
strcat(defines, "#define OPT_SHADOW_ONETAP\n"); strcat(defines, "#define OPT_SHADOW_ONETAP\n");

View File

@@ -1,15 +1,10 @@
#include <string.h> #include <string.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h>
#include <pwd.h> #include <pwd.h>
#include <pthread.h>
#include <alsa/asoundlib.h>
// SDL2 include stuff
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#define GL_GLEXT_PROTOTYPES 1
#include <SDL2/SDL_opengles2.h>
//
#include "game.h" #include "game.h"
@@ -25,84 +20,78 @@ int osGetTime() {
} }
// sound // sound
snd_pcm_uframes_t SND_FRAMES = 512;
snd_pcm_t *sndOut; int SND_FRAMES = 512;
// A Frame is a struct containing: int16 L, int16 R.
Sound::Frame *sndData; Sound::Frame *sndData;
pthread_t sndThread; SDL_AudioDeviceID sdl_audiodev;
void* sndFill(void *arg) { void sndFill(void *udata, Uint8 *stream, int len) {
while (sndOut) { // Let's milk the audio subsystem for SND_FRAMES frames!
Sound::fill(sndData, SND_FRAMES); Sound::fill(sndData, SND_FRAMES);
// We have the number of samples, but each sample is sizeof(Sound::Frame) bytes long,
int err = snd_pcm_writei(sndOut, sndData, SND_FRAMES); // and memcpy copies a number of bytes.
if (err < 0) { memcpy (stream, sndData, SND_FRAMES * sizeof(Sound::Frame));
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() { bool sndInit() {
unsigned int freq = 44100; int FREQ = 44100;
int err; SDL_AudioSpec desired, obtained;
if ((err = snd_pcm_open(&sndOut, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
LOG("! sound: open %s\n", snd_strerror(err));\ desired.freq = FREQ;
sndOut = NULL; desired.format = AUDIO_S16SYS;
desired.channels = 2;
desired.samples = SND_FRAMES;
desired.callback = sndFill;
desired.userdata = NULL;
sdl_audiodev = SDL_OpenAudioDevice(NULL, 0, &desired, &obtained, /*SDL_AUDIO_ALLOW_FORMAT_CHANGE*/0);
if (sdl_audiodev == 0)
{
LOG ("SDL2: error opening audio device: %s\n", SDL_GetError());
return false; return false;
} }
snd_pcm_hw_params_t *params; if (desired.samples != obtained.samples) {
LOG ("SDL2: number of samples not supported by device. Watch out for buggy audio drivers!\n");
snd_pcm_hw_params_alloca(&params); return false;
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); // Initialize audio buffer and fill it with zeros
pthread_create(&sndThread, NULL, sndFill, NULL); sndData = new Sound::Frame[SND_FRAMES];
memset(sndData, 0, SND_FRAMES * sizeof(Sound::Frame));
SDL_PauseAudioDevice(sdl_audiodev,0);
return true; return true;
} }
void sndFree() { void sndFree() {
pthread_cancel(sndThread); SDL_PauseAudioDevice(sdl_audiodev,1);
snd_pcm_drop(sndOut); SDL_CloseAudioDevice(sdl_audiodev);
snd_pcm_drain(sndOut);
snd_pcm_close(sndOut); // Delete the audio buffer
delete[] sndData; delete[] sndData;
} }
// Input // Input
SDL_GameController *sdl_gamecontroller;
SDL_Joystick *sdl_joystick; #define MAX_JOYS 4
bool using_old_joystick_interface; #define JOY_DEAD_ZONE_STICK 8192
#define WIN_W 640
#define WIN_H 480
struct sdl_input *sdl_inputs;
int sdl_numjoysticks, sdl_numcontrollers;
SDL_Joystick *sdl_joysticks[MAX_JOYS];
SDL_GameController *sdl_controllers[MAX_JOYS];
SDL_Window *sdl_window;
SDL_Renderer *sdl_renderer;
SDL_DisplayMode sdl_displaymode;
bool fullscreen;
bool l_alt, k_enter; // Is left ALT pressed? (For fullscreen toggle purposes)
vec2 joyL, joyR; vec2 joyL, joyR;
@@ -114,6 +103,20 @@ void osJoyVibrate(int index, float L, float R) {
// TODO // TODO
} }
void toggleFullscreen () {
Uint32 flags = 0;
fullscreen = !fullscreen;
flags = fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0;
SDL_SetWindowFullscreen (sdl_window, flags);
// Tell the engine we have changed display size!
Core::width = fullscreen ? sdl_displaymode.w : WIN_W;
Core::height = fullscreen ? sdl_displaymode.h : WIN_H;
}
InputKey codeToInputKey(int code) { InputKey codeToInputKey(int code) {
switch (code) { switch (code) {
// keyboard // keyboard
@@ -123,13 +126,21 @@ InputKey codeToInputKey(int code) {
case SDL_SCANCODE_DOWN : return ikDown; case SDL_SCANCODE_DOWN : return ikDown;
case SDL_SCANCODE_SPACE : return ikSpace; case SDL_SCANCODE_SPACE : return ikSpace;
case SDL_SCANCODE_TAB : return ikTab; case SDL_SCANCODE_TAB : return ikTab;
case SDL_SCANCODE_RETURN : return ikEnter;
case SDL_SCANCODE_RETURN : {
k_enter = !k_enter;
if (l_alt && k_enter) {
toggleFullscreen();
}
else return ikEnter;
}
case SDL_SCANCODE_ESCAPE : return ikEscape; case SDL_SCANCODE_ESCAPE : return ikEscape;
case SDL_SCANCODE_LSHIFT : case SDL_SCANCODE_LSHIFT :
case SDL_SCANCODE_RSHIFT : return ikShift; case SDL_SCANCODE_RSHIFT : return ikShift;
case SDL_SCANCODE_LCTRL : case SDL_SCANCODE_LCTRL :
case SDL_SCANCODE_RCTRL : return ikCtrl; case SDL_SCANCODE_RCTRL : return ikCtrl;
case SDL_SCANCODE_LALT : case SDL_SCANCODE_LALT : { l_alt = !l_alt; } // We toggle l_alt being pushed.
case SDL_SCANCODE_RALT : return ikAlt; case SDL_SCANCODE_RALT : return ikAlt;
case SDL_SCANCODE_0 : return ik0; case SDL_SCANCODE_0 : return ik0;
case SDL_SCANCODE_1 : return ik1; case SDL_SCANCODE_1 : return ik1;
@@ -172,9 +183,9 @@ InputKey codeToInputKey(int code) {
return ikNone; return ikNone;
} }
JoyKey codeToJoyKey(int code) { JoyKey controllerCodeToJoyKey(int code) {
// joystick using the modern SDL GameController interface
switch (code) { switch (code) {
// gamepad
case SDL_CONTROLLER_BUTTON_A : return jkA; case SDL_CONTROLLER_BUTTON_A : return jkA;
case SDL_CONTROLLER_BUTTON_B : return jkB; case SDL_CONTROLLER_BUTTON_B : return jkB;
case SDL_CONTROLLER_BUTTON_X : return jkX; case SDL_CONTROLLER_BUTTON_X : return jkX;
@@ -189,34 +200,55 @@ JoyKey codeToJoyKey(int code) {
return jkNone; 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() { bool inputInit() {
sdl_gamecontroller = NULL; int i;
sdl_joystick = NULL; joyL = joyR = vec2(0);
if (SDL_NumJoysticks() > 0) { sdl_numjoysticks = SDL_NumJoysticks();
if(SDL_IsGameController(0)) { sdl_numjoysticks = (sdl_numjoysticks < MAX_JOYS )? sdl_numjoysticks : MAX_JOYS;
sdl_gamecontroller = SDL_GameControllerOpen(0); 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 { else {
sdl_joystick = SDL_JoystickOpen(0); sdl_joysticks[i] = SDL_JoystickOpen(i);
using_old_joystick_interface = true;
} }
} }
return true; return true;
} }
void inputFree() { void inputFree() {
if (sdl_gamecontroller != NULL) { int i;
SDL_GameControllerClose(sdl_gamecontroller); for (i = 0; i < sdl_numcontrollers; i++) {
sdl_gamecontroller = NULL; SDL_GameControllerClose(sdl_controllers[i]);
} }
if (sdl_joystick != NULL) { for (i = 0; i < sdl_numjoysticks; i++) {
SDL_JoystickClose(sdl_joystick); SDL_JoystickClose(sdl_joysticks[i]);
sdl_joystick = NULL;
} }
} }
#define JOY_DEAD_ZONE_STICK 8192
float joyAxisValue(int value) { float joyAxisValue(int value) {
if (value > -JOY_DEAD_ZONE_STICK && value < JOY_DEAD_ZONE_STICK) if (value > -JOY_DEAD_ZONE_STICK && value < JOY_DEAD_ZONE_STICK)
return 0.0f; return 0.0f;
@@ -232,12 +264,22 @@ vec2 joyDir(const vec2 &value) {
return value.normal() * dist; 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() { void inputUpdate() {
// get input events // get input events
int joyIndex = 0; // TODO: joy index int joyIndex;
SDL_Event event; SDL_Event event;
while (SDL_PollEvent(&event) == 1) { // while there are still events to be processed while (SDL_PollEvent(&event) == 1) { // while there are still events to be processed
switch (event.type) { switch (event.type) {
case SDL_KEYDOWN: { case SDL_KEYDOWN: {
@@ -256,85 +298,98 @@ void inputUpdate() {
} }
break; break;
} }
// Joystick reading using the modern GameController interface
case SDL_CONTROLLERBUTTONDOWN: { case SDL_CONTROLLERBUTTONDOWN: {
JoyKey key = codeToJoyKey(event.cbutton.button); joyIndex = getJoyIndex(event.cbutton.which);
JoyKey key = controllerCodeToJoyKey(event.cbutton.button);
Input::setJoyDown(joyIndex, key, 1); Input::setJoyDown(joyIndex, key, 1);
break; break;
} }
case SDL_CONTROLLERBUTTONUP: { case SDL_CONTROLLERBUTTONUP: {
JoyKey key = codeToJoyKey(event.cbutton.button); joyIndex = getJoyIndex(event.cbutton.which);
JoyKey key = controllerCodeToJoyKey(event.cbutton.button);
Input::setJoyDown(joyIndex, key, 0); Input::setJoyDown(joyIndex, key, 0);
break; break;
} }
case SDL_CONTROLLERAXISMOTION: { case SDL_CONTROLLERAXISMOTION: {
joyIndex = getJoyIndex(event.caxis.which);
switch (event.caxis.axis) { switch (event.caxis.axis) {
case SDL_CONTROLLER_AXIS_LEFTX: case SDL_CONTROLLER_AXIS_LEFTX: joyL.x = joyAxisValue(event.caxis.value); break;
if (event.caxis.value < 0) { case SDL_CONTROLLER_AXIS_LEFTY: joyL.y = joyAxisValue(event.caxis.value); break;
Input::setJoyDown(joyIndex, jkLeft, 1); case SDL_CONTROLLER_AXIS_RIGHTX: joyR.x = joyAxisValue(event.caxis.value); break;
Input::setJoyDown(joyIndex, jkRight, 0); case SDL_CONTROLLER_AXIS_RIGHTY: joyR.y = joyAxisValue(event.caxis.value); break;
} }
if (event.caxis.value > 0) {
Input::setJoyDown(joyIndex, jkRight, 1);
Input::setJoyDown(joyIndex, jkLeft, 0);
}
if (event.caxis.value == 0) {
Input::setJoyDown(joyIndex, jkRight, 0);
Input::setJoyDown(joyIndex, jkLeft, 0);
}
break;
case SDL_CONTROLLER_AXIS_LEFTY:
if (event.caxis.value < 0) {
Input::setJoyDown(joyIndex, jkUp, 1);
Input::setJoyDown(joyIndex, jkDown, 0);
}
if (event.caxis.value > 0) {
Input::setJoyDown(joyIndex, jkUp, 0);
Input::setJoyDown(joyIndex, jkDown, 1);
}
if (event.caxis.value == 0) {
Input::setJoyDown(joyIndex, jkUp, 0);
Input::setJoyDown(joyIndex, jkDown, 0);
}
break;
}
break;
}
}
Input::setJoyPos(joyIndex, jkL, joyDir(joyL)); Input::setJoyPos(joyIndex, jkL, joyDir(joyL));
Input::setJoyPos(joyIndex, jkR, joyDir(joyR)); 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 main(int argc, char **argv) {
int w, h; int w, h;
SDL_DisplayMode current;
SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_EVENTS|SDL_INIT_GAMECONTROLLER);
SDL_Init(SDL_INIT_VIDEO|SDL_INIT_EVENTS|SDL_INIT_GAMECONTROLLER); SDL_GetCurrentDisplayMode(0, &sdl_displaymode);
SDL_GetCurrentDisplayMode(0, &current); #ifdef _GAPI_GLES
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
#endif
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
// We start in fullscreen mode using the vide mode currently in use, to avoid video mode changes. // 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, sdl_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); //sdl_displaymode.w, sdl_displaymode.h, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_FULLSCREEN_DESKTOP);
640, 480, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN); WIN_W, WIN_H, 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. // 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); SDL_GetWindowSize(sdl_window, &w, &h);
Core::width = w; Core::width = w;
Core::height = h; Core::height = h;
SDL_GLContext context = SDL_GL_CreateContext(window); SDL_GLContext context = SDL_GL_CreateContext(sdl_window);
SDL_GL_SetSwapInterval(0); SDL_GL_SetSwapInterval(0);
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, sdl_renderer = SDL_CreateRenderer(sdl_window, -1,
SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
SDL_ShowCursor(SDL_DISABLE); SDL_ShowCursor(SDL_DISABLE);
@@ -370,15 +425,15 @@ int main(int argc, char **argv) {
if (Game::update()) { if (Game::update()) {
Game::render(); Game::render();
Core::waitVBlank(); Core::waitVBlank();
SDL_GL_SwapWindow(window); SDL_GL_SwapWindow(sdl_window);
} }
}; };
sndFree(); sndFree();
Game::deinit(); Game::deinit();
SDL_DestroyRenderer(renderer); SDL_DestroyRenderer(sdl_renderer);
SDL_DestroyWindow(window); SDL_DestroyWindow(sdl_window);
SDL_Quit(); SDL_Quit();
return 0; return 0;