From: Holger Schemel Date: Fri, 30 Jun 2017 18:25:05 +0000 (+0200) Subject: improved support for game controllers and joysticks X-Git-Tag: 4.0.1.0~38 X-Git-Url: https://git.artsoft.org/?a=commitdiff_plain;h=cad3c0cde6926a8a379725b1ffc6fc657272a76d;p=rocksndiamonds.git improved support for game controllers and joysticks --- diff --git a/src/events.c b/src/events.c index 6dbb1e71..fc34d898 100644 --- a/src/events.c +++ b/src/events.c @@ -247,6 +247,11 @@ void HandleOtherEvents(Event *event) break; #if defined(TARGET_SDL) +#if defined(TARGET_SDL2) + case SDL_CONTROLLERAXISMOTION: + case SDL_CONTROLLERBUTTONDOWN: + case SDL_CONTROLLERBUTTONUP: +#endif case SDL_JOYAXISMOTION: case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP: @@ -1930,23 +1935,30 @@ static int HandleJoystickForAllPlayers() { int i; int result = 0; + boolean no_joysticks_configured = TRUE; + boolean use_as_joystick_nr = (game_status != GAME_MODE_PLAYING); + static byte joy_action_last[MAX_PLAYERS]; + + for (i = 0; i < MAX_PLAYERS; i++) + if (setup.input[i].use_joystick) + no_joysticks_configured = FALSE; + + /* if no joysticks configured, map connected joysticks to players */ + if (no_joysticks_configured) + use_as_joystick_nr = TRUE; for (i = 0; i < MAX_PLAYERS; i++) { byte joy_action = 0; - /* - if (!setup.input[i].use_joystick) - continue; - */ - - joy_action = Joystick(i); + joy_action = JoystickExt(i, use_as_joystick_nr); result |= joy_action; - if (!setup.input[i].use_joystick) - continue; + if ((setup.input[i].use_joystick || no_joysticks_configured) && + joy_action != joy_action_last[i]) + stored_player[i].action = joy_action; - stored_player[i].action = joy_action; + joy_action_last[i] = joy_action; } return result; diff --git a/src/game.c b/src/game.c index 8d2ed466..89e0c110 100644 --- a/src/game.c +++ b/src/game.c @@ -1619,7 +1619,6 @@ void GetPlayerConfig() setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music); SetAudioMode(setup.sound); - InitJoysticks(); } int GetElementFromGroupElement(int element) diff --git a/src/libgame/joystick.c b/src/libgame/joystick.c index 92eec0e3..a6a065cd 100644 --- a/src/libgame/joystick.c +++ b/src/libgame/joystick.c @@ -159,7 +159,7 @@ void CheckJoystickData() } } -int Joystick(int player_nr) +int JoystickExt(int player_nr, boolean use_as_joystick_nr) { int joystick_fd = joystick.fd[player_nr]; int js_x, js_y; @@ -167,10 +167,13 @@ int Joystick(int player_nr) int left, right, up, down; int result = JOY_NO_ACTION; + if (use_as_joystick_nr) + joystick_fd = player_nr; + if (joystick.status != JOYSTICK_ACTIVATED) return JOY_NO_ACTION; - if (joystick_fd < 0 || !setup.input[player_nr].use_joystick) + if (joystick_fd < 0) return JOY_NO_ACTION; if (!ReadJoystick(joystick_fd, &js_x, &js_y, &js_b1, &js_b2)) @@ -208,10 +211,15 @@ int Joystick(int player_nr) return result; } -int JoystickButton(int player_nr) +int Joystick(int player_nr) +{ + return JoystickExt(player_nr, FALSE); +} + +int JoystickButtonExt(int player_nr, boolean use_as_joystick_nr) { static int last_joy_button[MAX_PLAYERS] = { 0, 0, 0, 0 }; - int joy_button = (Joystick(player_nr) & JOY_BUTTON); + int joy_button = (JoystickExt(player_nr, use_as_joystick_nr) & JOY_BUTTON); int result; if (joy_button) @@ -233,13 +241,18 @@ int JoystickButton(int player_nr) return result; } +int JoystickButton(int player_nr) +{ + return JoystickButtonExt(player_nr, FALSE); +} + int AnyJoystick() { int i; int result = 0; for (i = 0; i < MAX_PLAYERS; i++) - result |= Joystick(i); + result |= JoystickExt(i, TRUE); return result; } @@ -251,7 +264,7 @@ int AnyJoystickButton() for (i = 0; i < MAX_PLAYERS; i++) { - result = JoystickButton(i); + result = JoystickButtonExt(i, TRUE); if (result != JOY_BUTTON_NOT_PRESSED) break; } diff --git a/src/libgame/joystick.h b/src/libgame/joystick.h index c7f65feb..f3be2954 100644 --- a/src/libgame/joystick.h +++ b/src/libgame/joystick.h @@ -34,21 +34,14 @@ /* get these values from the program 'js' from the joystick package, */ /* set JOYSTICK_PERCENT to a threshold appropriate for your joystick */ -#if defined(TARGET_SDL) -#define JOYSTICK_XLEFT -32767 +#define JOYSTICK_MAX_AXIS_POS 32767 + +#define JOYSTICK_XLEFT -JOYSTICK_MAX_AXIS_POS #define JOYSTICK_XMIDDLE 0 -#define JOYSTICK_XRIGHT 32767 -#define JOYSTICK_YUPPER -32767 +#define JOYSTICK_XRIGHT +JOYSTICK_MAX_AXIS_POS +#define JOYSTICK_YUPPER -JOYSTICK_MAX_AXIS_POS #define JOYSTICK_YMIDDLE 0 -#define JOYSTICK_YLOWER 32767 -#else -#define JOYSTICK_XLEFT 1 -#define JOYSTICK_XMIDDLE 128 -#define JOYSTICK_XRIGHT 255 -#define JOYSTICK_YUPPER 1 -#define JOYSTICK_YMIDDLE 128 -#define JOYSTICK_YLOWER 255 -#endif +#define JOYSTICK_YLOWER +JOYSTICK_MAX_AXIS_POS #define JOYSTICK_PERCENT 25 @@ -77,6 +70,7 @@ char *getDeviceNameFromJoystickNr(int); void CheckJoystickData(void); int Joystick(int); +int JoystickExt(int, boolean); int JoystickButton(int); int AnyJoystick(void); int AnyJoystickButton(void); diff --git a/src/libgame/sdl.c b/src/libgame/sdl.c index e275149f..5586628b 100644 --- a/src/libgame/sdl.c +++ b/src/libgame/sdl.c @@ -2591,16 +2591,42 @@ void SDLHandleWindowManagerEvent(Event *event) /* joystick functions */ /* ========================================================================= */ -static SDL_Joystick *sdl_joystick[MAX_PLAYERS] = { NULL, NULL, NULL, NULL }; -static int sdl_js_axis[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} }; -static int sdl_js_button[MAX_PLAYERS][2] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} }; +#if defined(TARGET_SDL2) +static void *sdl_joystick[MAX_PLAYERS]; // game controller or joystick +#else +static SDL_Joystick *sdl_joystick[MAX_PLAYERS]; // only joysticks supported +#endif +static int sdl_js_axis_raw[MAX_PLAYERS][2]; +static int sdl_js_axis[MAX_PLAYERS][2]; +static int sdl_js_button[MAX_PLAYERS][2]; +static boolean sdl_is_controller[MAX_PLAYERS]; static boolean SDLOpenJoystick(int nr) { if (nr < 0 || nr > MAX_PLAYERS) return FALSE; - return ((sdl_joystick[nr] = SDL_JoystickOpen(nr)) == NULL ? FALSE : TRUE); +#if defined(TARGET_SDL2) + sdl_is_controller[nr] = SDL_IsGameController(nr); +#else + sdl_is_controller[nr] = FALSE; +#endif + +#if 1 + Error(ERR_DEBUG, "opening joystick %d (%s)", + nr, (sdl_is_controller[nr] ? "game controller" : "joystick")); +#endif + +#if defined(TARGET_SDL2) + if (sdl_is_controller[nr]) + sdl_joystick[nr] = SDL_GameControllerOpen(nr); + else + sdl_joystick[nr] = SDL_JoystickOpen(nr); +#else + sdl_joystick[nr] = SDL_JoystickOpen(nr); +#endif + + return (sdl_joystick[nr] != NULL); } static void SDLCloseJoystick(int nr) @@ -2608,12 +2634,30 @@ static void SDLCloseJoystick(int nr) if (nr < 0 || nr > MAX_PLAYERS) return; +#if 1 + Error(ERR_DEBUG, "closing joystick %d", nr); +#endif + +#if defined(TARGET_SDL2) + if (sdl_is_controller[nr]) + SDL_GameControllerClose(sdl_joystick[nr]); + else + SDL_JoystickClose(sdl_joystick[nr]); +#else SDL_JoystickClose(sdl_joystick[nr]); +#endif sdl_joystick[nr] = NULL; + + sdl_js_axis_raw[nr][0] = -1; + sdl_js_axis_raw[nr][1] = -1; + sdl_js_axis[nr][0] = 0; + sdl_js_axis[nr][1] = 0; + sdl_js_button[nr][0] = 0; + sdl_js_button[nr][1] = 0; } -static boolean SDLCheckJoystickOpened(int nr) +boolean SDLCheckJoystickOpened(int nr) { if (nr < 0 || nr > MAX_PLAYERS) return FALSE; @@ -2625,23 +2669,142 @@ static boolean SDLCheckJoystickOpened(int nr) #endif } +static void setJoystickAxis(int nr, int axis_id_raw, int axis_value) +{ +#if defined(TARGET_SDL2) + int axis_id = (axis_id_raw == SDL_CONTROLLER_AXIS_LEFTX || + axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTX ? 0 : + axis_id_raw == SDL_CONTROLLER_AXIS_LEFTY || + axis_id_raw == SDL_CONTROLLER_AXIS_RIGHTY ? 1 : -1); +#else + int axis_id = axis_id_raw % 2; +#endif + + if (axis_id == -1) + return; + + // prevent (slightly jittering, but centered) axis A from resetting axis B + if (ABS(axis_value) < JOYSTICK_PERCENT * JOYSTICK_MAX_AXIS_POS / 100 && + axis_id_raw != sdl_js_axis_raw[nr][axis_id]) + return; + + sdl_js_axis[nr][axis_id] = axis_value; + sdl_js_axis_raw[nr][axis_id] = axis_id_raw; +} + +static void setJoystickButton(int nr, int button_id_raw, int button_state) +{ +#if defined(TARGET_SDL2) + int button_id = (button_id_raw == SDL_CONTROLLER_BUTTON_A || + button_id_raw == SDL_CONTROLLER_BUTTON_X || + button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSHOULDER || + button_id_raw == SDL_CONTROLLER_BUTTON_LEFTSTICK || + button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSTICK ? 0 : + button_id_raw == SDL_CONTROLLER_BUTTON_B || + button_id_raw == SDL_CONTROLLER_BUTTON_Y || + button_id_raw == SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ? 1 : + -1); + + if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT) + sdl_js_axis[nr][0] = button_state * JOYSTICK_XLEFT; + else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) + sdl_js_axis[nr][0] = button_state * JOYSTICK_XRIGHT; + else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP) + sdl_js_axis[nr][1] = button_state * JOYSTICK_YUPPER; + else if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + sdl_js_axis[nr][1] = button_state * JOYSTICK_YLOWER; + + if (button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_LEFT || + button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_RIGHT || + button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_UP || + button_id_raw == SDL_CONTROLLER_BUTTON_DPAD_DOWN) + sdl_js_axis_raw[nr][0] = sdl_js_axis_raw[nr][1] = -1; +#else + int button_id = button_id_raw % 2; +#endif + + if (button_id == -1) + return; + + sdl_js_button[nr][button_id] = button_state; +} + void HandleJoystickEvent(Event *event) { switch(event->type) { +#if defined(TARGET_SDL2) + case SDL_CONTROLLERAXISMOTION: +#if 1 + Error(ERR_DEBUG, "SDL_CONTROLLERAXISMOTION: device %d, axis %d: %d", + event->caxis.which, event->caxis.axis, event->caxis.value); +#endif + setJoystickAxis(event->caxis.which, + event->caxis.axis, + event->caxis.value); + break; + + case SDL_CONTROLLERBUTTONDOWN: +#if 1 + Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONDOWN: device %d, button %d", + event->cbutton.which, event->cbutton.button); +#endif + setJoystickButton(event->cbutton.which, + event->cbutton.button, + TRUE); + break; + + case SDL_CONTROLLERBUTTONUP: +#if 1 + Error(ERR_DEBUG, "SDL_CONTROLLERBUTTONUP: device %d, button %d", + event->cbutton.which, event->cbutton.button); +#endif + setJoystickButton(event->cbutton.which, + event->cbutton.button, + FALSE); + break; +#endif + case SDL_JOYAXISMOTION: - if (event->jaxis.axis < 2) - sdl_js_axis[event->jaxis.which][event->jaxis.axis]= event->jaxis.value; + if (sdl_is_controller[event->jaxis.which]) + break; + +#if 1 + Error(ERR_DEBUG, "SDL_JOYAXISMOTION: device %d, axis %d: %d", + event->jaxis.which, event->jaxis.axis, event->jaxis.value); +#endif + if (event->jaxis.axis < 4) + setJoystickAxis(event->jaxis.which, + event->jaxis.axis, + event->jaxis.value); break; case SDL_JOYBUTTONDOWN: - if (event->jbutton.button < 2) - sdl_js_button[event->jbutton.which][event->jbutton.button] = TRUE; + if (sdl_is_controller[event->jaxis.which]) + break; + +#if 1 + Error(ERR_DEBUG, "SDL_JOYBUTTONDOWN: device %d, button %d", + event->jbutton.which, event->jbutton.button); +#endif + if (event->jbutton.button < 4) + setJoystickButton(event->jbutton.which, + event->jbutton.button, + TRUE); break; case SDL_JOYBUTTONUP: - if (event->jbutton.button < 2) - sdl_js_button[event->jbutton.which][event->jbutton.button] = FALSE; + if (sdl_is_controller[event->jaxis.which]) + break; + +#if 1 + Error(ERR_DEBUG, "SDL_JOYBUTTONUP: device %d, button %d", + event->jbutton.which, event->jbutton.button); +#endif + if (event->jbutton.button < 4) + setJoystickButton(event->jbutton.which, + event->jbutton.button, + FALSE); break; default: @@ -2653,19 +2816,60 @@ void SDLInitJoysticks() { static boolean sdl_joystick_subsystem_initialized = FALSE; boolean print_warning = !sdl_joystick_subsystem_initialized; +#if defined(TARGET_SDL2) + char *mappings_file = "gamecontrollerdb.txt"; + int num_mappings; +#endif int i; if (!sdl_joystick_subsystem_initialized) { sdl_joystick_subsystem_initialized = TRUE; +#if defined(TARGET_SDL2) + SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0"); + + if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0) +#else if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) +#endif { Error(ERR_EXIT, "SDL_Init() failed: %s", SDL_GetError()); return; } + +#if defined(TARGET_SDL2) + num_mappings = SDL_GameControllerAddMappingsFromFile(mappings_file); + + if (num_mappings != -1) + Error(ERR_INFO, "%d game controller mapping(s) added", num_mappings); + else + Error(ERR_WARN, "no game controller mappings found"); + + Error(ERR_INFO, "%d joystick(s) found:", SDL_NumJoysticks()); + + for (i = 0; i < SDL_NumJoysticks(); i++) + { + const char *name, *type; + + if (SDL_IsGameController(i)) + { + name = SDL_GameControllerNameForIndex(i); + type = "game controller"; + } + else + { + name = SDL_JoystickNameForIndex(i); + type = "joystick"; + } + + Error(ERR_INFO, "- joystick %d (%s): '%s'", + i, type, (name ? name : "(Unknown)")); + } +#endif } + /* assign joysticks from configured to connected joystick for all players */ for (i = 0; i < MAX_PLAYERS; i++) { /* get configured joystick for this player */ @@ -2682,26 +2886,19 @@ void SDLInitJoysticks() /* misuse joystick file descriptor variable to store joystick number */ joystick.fd[i] = joystick_nr; + } - if (joystick_nr == -1) - continue; - + /* now open all connected joysticks (regardless if configured or not) */ + for (i = 0; i < SDL_NumJoysticks(); i++) + { /* this allows subsequent calls to 'InitJoysticks' for re-initialization */ - if (SDLCheckJoystickOpened(joystick_nr)) - SDLCloseJoystick(joystick_nr); - - if (!setup.input[i].use_joystick) - continue; - - if (!SDLOpenJoystick(joystick_nr)) - { - if (print_warning) - Error(ERR_WARN, "cannot open joystick %d", joystick_nr); - - continue; - } + if (SDLCheckJoystickOpened(i)) + SDLCloseJoystick(i); - joystick.status = JOYSTICK_ACTIVATED; + if (SDLOpenJoystick(i)) + joystick.status = JOYSTICK_ACTIVATED; + else if (print_warning) + Error(ERR_WARN, "cannot open joystick %d", i); } } diff --git a/src/libgame/sdl.h b/src/libgame/sdl.h index c88b6a82..f9fc1a87 100644 --- a/src/libgame/sdl.h +++ b/src/libgame/sdl.h @@ -484,6 +484,7 @@ void SDLHandleWindowManagerEvent(Event *); void HandleJoystickEvent(Event *); void SDLInitJoysticks(void); boolean SDLReadJoystick(int, int *, int *, boolean *, boolean *); +boolean SDLCheckJoystickOpened(int); void PrepareFadeBitmap(int); diff --git a/src/libgame/system.c b/src/libgame/system.c index b5de8c9e..de59d77a 100644 --- a/src/libgame/system.c +++ b/src/libgame/system.c @@ -1629,3 +1629,8 @@ boolean ReadJoystick(int nr, int *x, int *y, boolean *b1, boolean *b2) { return SDLReadJoystick(nr, x, y, b1, b2); } + +boolean CheckJoystickOpened(int nr) +{ + return SDLCheckJoystickOpened(nr); +} diff --git a/src/libgame/system.h b/src/libgame/system.h index be19fccd..3fc4eb6c 100644 --- a/src/libgame/system.h +++ b/src/libgame/system.h @@ -1544,5 +1544,6 @@ boolean CheckCloseWindowEvent(ClientMessageEvent *); void InitJoysticks(); boolean ReadJoystick(int, int *, int *, boolean *, boolean *); +boolean CheckJoystickOpened(int); #endif /* SYSTEM_H */ diff --git a/src/screens.c b/src/screens.c index dbbd5075..c241ab3e 100644 --- a/src/screens.c +++ b/src/screens.c @@ -1413,7 +1413,6 @@ void DrawMainMenu() ExpireSoundLoops(FALSE); KeyboardAutoRepeatOn(); - ActivateJoystick(); audio.sound_deactivated = FALSE; @@ -4190,7 +4189,6 @@ void DrawHallOfFame(int highlight_position) /* (this is needed when called from GameEnd() after winning a game) */ KeyboardAutoRepeatOn(); - ActivateJoystick(); /* (this is needed when called from GameEnd() after winning a game) */ SetDrawDeactivationMask(REDRAW_NONE); @@ -5239,14 +5237,12 @@ static void execSetupChooseMusic() DrawSetupScreen(); } -#if !defined(PLATFORM_ANDROID) static void execSetupInput() { setup_mode = SETUP_MODE_INPUT; DrawSetupScreen(); } -#endif static void execSetupShortcuts() { @@ -5310,12 +5306,8 @@ static struct TokenInfo setup_info_main[] = { TYPE_ENTER_MENU, execSetupGraphics, "Graphics" }, { TYPE_ENTER_MENU, execSetupSound, "Sound & Music" }, { TYPE_ENTER_MENU, execSetupArtwork, "Custom Artwork" }, -#if !defined(PLATFORM_ANDROID) { TYPE_ENTER_MENU, execSetupInput, "Input Devices" }, { TYPE_ENTER_MENU, execSetupTouch, "Touch Controls" }, -#else - { TYPE_ENTER_MENU, execSetupTouch, "Touch Controls" }, -#endif { TYPE_ENTER_MENU, execSetupShortcuts, "Key Shortcuts" }, { TYPE_EMPTY, NULL, "" }, { TYPE_LEAVE_MENU, execExitSetup, "Exit" }, @@ -5956,9 +5948,6 @@ void DrawSetupScreen_Input() DrawTextSCentered(mSY - SY + 16, FONT_TITLE_1, "Setup Input"); - DrawTextSCentered(SYSIZE - 20, FONT_TITLE_2, - "Joysticks deactivated on this screen"); - for (i = 0; setup_info[i].type != 0 && i < MAX_MENU_ENTRIES_ON_SCREEN; i++) { if (setup_info[i].type & (TYPE_ENTER_MENU|TYPE_ENTER_LIST)) @@ -6030,8 +6019,6 @@ static void drawPlayerSetupInputInfo(int player_nr, boolean active) }; int text_font_nr = (active ? FONT_MENU_1_ACTIVE : FONT_MENU_1); - InitJoysticks(); - custom_key = setup.input[player_nr].key; DrawText(mSX + 11 * 32, mSY + 2 * 32, int2str(player_nr + 1, 1), @@ -6045,8 +6032,10 @@ static void drawPlayerSetupInputInfo(int player_nr, boolean active) if (setup.input[player_nr].use_joystick) { char *device_name = setup.input[player_nr].joy.device_name; - char *text = joystick_name[getJoystickNrFromDeviceName(device_name)]; - int font_nr = (joystick.fd[player_nr] < 0 ? FONT_VALUE_OLD : FONT_VALUE_1); + int joystick_nr = getJoystickNrFromDeviceName(device_name); + boolean joystick_active = CheckJoystickOpened(joystick_nr); + char *text = joystick_name[joystick_nr]; + int font_nr = (joystick_active ? FONT_VALUE_1 : FONT_VALUE_OLD); DrawText(mSX + 8 * 32, mSY + 3 * 32, text, font_nr); DrawText(mSX + 32, mSY + 4 * 32, "Calibrate", text_font_nr); @@ -6201,10 +6190,7 @@ void HandleSetupScreen_Input(int mx, int my, int dx, int dy, int button) else if (y == 2) { if (setup.input[input_player_nr].use_joystick) - { - InitJoysticks(); CalibrateJoystick(input_player_nr); - } else CustomizeKeyboard(input_player_nr); } @@ -6544,8 +6530,6 @@ void CalibrateJoystick(int player_nr) void DrawSetupScreen() { - DeactivateJoystick(); - if (setup_mode == SETUP_MODE_INPUT) DrawSetupScreen_Input(); else if (setup_mode == SETUP_MODE_CHOOSE_GAME_SPEED)