Merge branch 'internal-hotplug-stableid' into 'master'

Controller hotplug support.

See merge request STJr/SRB2Internal!342
This commit is contained in:
toaster 2019-09-27 08:50:34 -04:00
commit c1dea190a8
7 changed files with 457 additions and 167 deletions

View file

@ -246,20 +246,20 @@ INT32 cv_debug;
consvar_t cv_usemouse = {"use_mouse", "On", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_usemouse = {"use_mouse", "On", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_usemouse2 = {"use_mouse2", "Off", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse2, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_usemouse2 = {"use_mouse2", "Off", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse2, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_usejoystick = {"use_joystick", "1", CV_SAVE|CV_CALL, usejoystick_cons_t, consvar_t cv_usejoystick = {"use_gamepad", "1", CV_SAVE|CV_CALL, usejoystick_cons_t,
I_InitJoystick, 0, NULL, NULL, 0, 0, NULL}; I_InitJoystick, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_usejoystick2 = {"use_joystick2", "2", CV_SAVE|CV_CALL, usejoystick_cons_t, consvar_t cv_usejoystick2 = {"use_gamepad2", "2", CV_SAVE|CV_CALL, usejoystick_cons_t,
I_InitJoystick2, 0, NULL, NULL, 0, 0, NULL}; I_InitJoystick2, 0, NULL, NULL, 0, 0, NULL};
#if (defined (LJOYSTICK) || defined (HAVE_SDL)) #if (defined (LJOYSTICK) || defined (HAVE_SDL))
#ifdef LJOYSTICK #ifdef LJOYSTICK
consvar_t cv_joyport = {"joyport", "/dev/js0", CV_SAVE, joyport_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_joyport = {"padport", "/dev/js0", CV_SAVE, joyport_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_joyport2 = {"joyport2", "/dev/js0", CV_SAVE, joyport_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; //Alam: for later consvar_t cv_joyport2 = {"padport2", "/dev/js0", CV_SAVE, joyport_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; //Alam: for later
#endif #endif
consvar_t cv_joyscale = {"joyscale", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_joyscale = {"padscale", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_joyscale2 = {"joyscale2", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale2, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_joyscale2 = {"padscale2", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale2, 0, NULL, NULL, 0, 0, NULL};
#else #else
consvar_t cv_joyscale = {"joyscale", "1", CV_SAVE|CV_HIDEN, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; //Alam: Dummy for save consvar_t cv_joyscale = {"padscale", "1", CV_SAVE|CV_HIDEN, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; //Alam: Dummy for save
consvar_t cv_joyscale2 = {"joyscale2", "1", CV_SAVE|CV_HIDEN, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; //Alam: Dummy for save consvar_t cv_joyscale2 = {"padscale2", "1", CV_SAVE|CV_HIDEN, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; //Alam: Dummy for save
#endif #endif
#if (defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON) #if (defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON)
consvar_t cv_mouse2port = {"mouse2port", "/dev/gpmdata", CV_SAVE, mouse2port_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_mouse2port = {"mouse2port", "/dev/gpmdata", CV_SAVE, mouse2port_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};

View file

@ -506,6 +506,9 @@ INT32 I_GetKey(void);
#define max(x, y) (((x) > (y)) ? (x) : (y)) #define max(x, y) (((x) > (y)) ? (x) : (y))
#endif #endif
// Max gamepad/joysticks that can be detected/used.
#define MAX_JOYSTICKS 4
// Floating point comparison epsilons from float.h // Floating point comparison epsilons from float.h
#ifndef FLT_EPSILON #ifndef FLT_EPSILON
#define FLT_EPSILON 1.1920928955078125e-7f #define FLT_EPSILON 1.1920928955078125e-7f

View file

@ -66,6 +66,13 @@
// And just some randomness for the exits. // And just some randomness for the exits.
#include "m_random.h" #include "m_random.h"
#if defined(HAVE_SDL)
#include "SDL.h"
#if SDL_VERSION_ATLEAST(2,0,0)
#include "sdl/sdlmain.h" // JOYSTICK_HOTPLUG
#endif
#endif
#ifdef PC_DOS #ifdef PC_DOS
#include <stdio.h> // for snprintf #include <stdio.h> // for snprintf
int snprintf(char *str, size_t n, const char *fmt, ...); int snprintf(char *str, size_t n, const char *fmt, ...);
@ -136,7 +143,7 @@ typedef enum
levellist_mode_t levellistmode = LLM_CREATESERVER; levellist_mode_t levellistmode = LLM_CREATESERVER;
UINT8 maplistoption = 0; UINT8 maplistoption = 0;
static char joystickInfo[8][29]; static char joystickInfo[MAX_JOYSTICKS+1][29];
#ifndef NONET #ifndef NONET
static UINT32 serverlistpage; static UINT32 serverlistpage;
#endif #endif
@ -1111,14 +1118,7 @@ static menuitem_t OP_Joystick2Menu[] =
{IT_STRING | IT_CVAR, NULL, "Third-Person Vert-Look", &cv_chasefreelook2, 130}, {IT_STRING | IT_CVAR, NULL, "Third-Person Vert-Look", &cv_chasefreelook2, 130},
}; };
static menuitem_t OP_JoystickSetMenu[] = static menuitem_t OP_JoystickSetMenu[1+MAX_JOYSTICKS];
{
{IT_CALL | IT_NOTHING, "None", NULL, M_AssignJoystick, '0'},
{IT_CALL | IT_NOTHING, "", NULL, M_AssignJoystick, '1'},
{IT_CALL | IT_NOTHING, "", NULL, M_AssignJoystick, '2'},
{IT_CALL | IT_NOTHING, "", NULL, M_AssignJoystick, '3'},
{IT_CALL | IT_NOTHING, "", NULL, M_AssignJoystick, '4'},
};
static menuitem_t OP_MouseOptionsMenu[] = static menuitem_t OP_MouseOptionsMenu[] =
{ {
@ -3543,6 +3543,8 @@ void M_Ticker(void)
// //
void M_Init(void) void M_Init(void)
{ {
int i;
CV_RegisterVar(&cv_nextmap); CV_RegisterVar(&cv_nextmap);
CV_RegisterVar(&cv_newgametype); CV_RegisterVar(&cv_newgametype);
CV_RegisterVar(&cv_chooseskin); CV_RegisterVar(&cv_chooseskin);
@ -3592,6 +3594,17 @@ void M_Init(void)
OP_ScreenshotOptionsMenu[op_screenshot_colorprofile].status = IT_GRAYEDOUT; OP_ScreenshotOptionsMenu[op_screenshot_colorprofile].status = IT_GRAYEDOUT;
#endif #endif
/*
Well the menu sucks for forcing us to have an item set
at all if every item just calls the same function, and
nothing more. Now just automate the definition.
*/
for (i = 0; i <= MAX_JOYSTICKS; ++i)
{
OP_JoystickSetMenu[i].status = ( IT_NOTHING|IT_CALL );
OP_JoystickSetMenu[i].itemaction = M_AssignJoystick;
}
#ifndef NONET #ifndef NONET
CV_RegisterVar(&cv_serversort); CV_RegisterVar(&cv_serversort);
#endif #endif
@ -9981,18 +9994,33 @@ static void M_ScreenshotOptions(INT32 choice)
static void M_DrawJoystick(void) static void M_DrawJoystick(void)
{ {
INT32 i; INT32 i, compareval2, compareval;
// draw title (or big pic) // draw title (or big pic)
M_DrawMenuTitle(); M_DrawMenuTitle();
for (i = 0; i <= 4; i++) // See MAX_JOYSTICKS for (i = 0; i <= MAX_JOYSTICKS; i++) // See MAX_JOYSTICKS
{ {
M_DrawTextBox(OP_JoystickSetDef.x-8, OP_JoystickSetDef.y+LINEHEIGHT*i-12, 28, 1); M_DrawTextBox(OP_JoystickSetDef.x-8, OP_JoystickSetDef.y+LINEHEIGHT*i-12, 28, 1);
//M_DrawSaveLoadBorder(OP_JoystickSetDef.x+4, OP_JoystickSetDef.y+1+LINEHEIGHT*i); //M_DrawSaveLoadBorder(OP_JoystickSetDef.x+4, OP_JoystickSetDef.y+1+LINEHEIGHT*i);
if ((setupcontrols_secondaryplayer && (i == cv_usejoystick2.value)) #ifdef JOYSTICK_HOTPLUG
|| (!setupcontrols_secondaryplayer && (i == cv_usejoystick.value))) if (atoi(cv_usejoystick2.string) > I_NumJoys())
compareval2 = atoi(cv_usejoystick2.string);
else
compareval2 = cv_usejoystick2.value;
if (atoi(cv_usejoystick.string) > I_NumJoys())
compareval = atoi(cv_usejoystick.string);
else
compareval = cv_usejoystick.value;
#else
compareval2 = cv_usejoystick2.value;
compareval = cv_usejoystick.value
#endif
if ((setupcontrols_secondaryplayer && (i == compareval2))
|| (!setupcontrols_secondaryplayer && (i == compareval)))
V_DrawString(OP_JoystickSetDef.x, OP_JoystickSetDef.y+LINEHEIGHT*i-4,V_GREENMAP,joystickInfo[i]); V_DrawString(OP_JoystickSetDef.x, OP_JoystickSetDef.y+LINEHEIGHT*i-4,V_GREENMAP,joystickInfo[i]);
else else
V_DrawString(OP_JoystickSetDef.x, OP_JoystickSetDef.y+LINEHEIGHT*i-4,0,joystickInfo[i]); V_DrawString(OP_JoystickSetDef.x, OP_JoystickSetDef.y+LINEHEIGHT*i-4,0,joystickInfo[i]);
@ -10005,7 +10033,7 @@ static void M_DrawJoystick(void)
} }
} }
static void M_SetupJoystickMenu(INT32 choice) void M_SetupJoystickMenu(INT32 choice)
{ {
INT32 i = 0; INT32 i = 0;
const char *joyNA = "Unavailable"; const char *joyNA = "Unavailable";
@ -10014,12 +10042,27 @@ static void M_SetupJoystickMenu(INT32 choice)
strcpy(joystickInfo[i], "None"); strcpy(joystickInfo[i], "None");
for (i = 1; i < 8; i++) for (i = 1; i <= MAX_JOYSTICKS; i++)
{ {
if (i <= n && (I_GetJoyName(i)) != NULL) if (i <= n && (I_GetJoyName(i)) != NULL)
strncpy(joystickInfo[i], I_GetJoyName(i), 28); strncpy(joystickInfo[i], I_GetJoyName(i), 28);
else else
strcpy(joystickInfo[i], joyNA); strcpy(joystickInfo[i], joyNA);
#ifdef JOYSTICK_HOTPLUG
// We use cv_usejoystick.string as the USER-SET var
// and cv_usejoystick.value as the INTERNAL var
//
// In practice, if cv_usejoystick.string == 0, this overrides
// cv_usejoystick.value and always disables
//
// Update cv_usejoystick.string here so that the user can
// properly change this value.
if (i == cv_usejoystick.value)
CV_SetValue(&cv_usejoystick, i);
if (i == cv_usejoystick2.value)
CV_SetValue(&cv_usejoystick2, i);
#endif
} }
M_SetupNextMenu(&OP_JoystickSetDef); M_SetupNextMenu(&OP_JoystickSetDef);
@ -10049,10 +10092,76 @@ static void M_Setup2PJoystickMenu(INT32 choice)
static void M_AssignJoystick(INT32 choice) static void M_AssignJoystick(INT32 choice)
{ {
#ifdef JOYSTICK_HOTPLUG
INT32 oldchoice, oldstringchoice;
INT32 numjoys = I_NumJoys();
if (setupcontrols_secondaryplayer)
{
oldchoice = oldstringchoice = atoi(cv_usejoystick2.string) > numjoys ? atoi(cv_usejoystick2.string) : cv_usejoystick2.value;
CV_SetValue(&cv_usejoystick2, choice);
// Just in case last-minute changes were made to cv_usejoystick.value,
// update the string too
// But don't do this if we're intentionally setting higher than numjoys
if (choice <= numjoys)
{
CV_SetValue(&cv_usejoystick2, cv_usejoystick2.value);
// reset this so the comparison is valid
if (oldchoice > numjoys)
oldchoice = cv_usejoystick2.value;
if (oldchoice != choice)
{
if (choice && oldstringchoice > numjoys) // if we did not select "None", we likely selected a used device
CV_SetValue(&cv_usejoystick2, (oldstringchoice > numjoys ? oldstringchoice : oldchoice));
if (oldstringchoice ==
(atoi(cv_usejoystick2.string) > numjoys ? atoi(cv_usejoystick2.string) : cv_usejoystick2.value))
M_StartMessage("This gamepad is used by another\n"
"player. Reset the gamepad\n"
"for that player first.\n\n"
"(Press a key)\n", NULL, MM_NOTHING);
}
}
}
else
{
oldchoice = oldstringchoice = atoi(cv_usejoystick.string) > numjoys ? atoi(cv_usejoystick.string) : cv_usejoystick.value;
CV_SetValue(&cv_usejoystick, choice);
// Just in case last-minute changes were made to cv_usejoystick.value,
// update the string too
// But don't do this if we're intentionally setting higher than numjoys
if (choice <= numjoys)
{
CV_SetValue(&cv_usejoystick, cv_usejoystick.value);
// reset this so the comparison is valid
if (oldchoice > numjoys)
oldchoice = cv_usejoystick.value;
if (oldchoice != choice)
{
if (choice && oldstringchoice > numjoys) // if we did not select "None", we likely selected a used device
CV_SetValue(&cv_usejoystick, (oldstringchoice > numjoys ? oldstringchoice : oldchoice));
if (oldstringchoice ==
(atoi(cv_usejoystick.string) > numjoys ? atoi(cv_usejoystick.string) : cv_usejoystick.value))
M_StartMessage("This gamepad is used by another\n"
"player. Reset the gamepad\n"
"for that player first.\n\n"
"(Press a key)\n", NULL, MM_NOTHING);
}
}
}
#else
if (setupcontrols_secondaryplayer) if (setupcontrols_secondaryplayer)
CV_SetValue(&cv_usejoystick2, choice); CV_SetValue(&cv_usejoystick2, choice);
else else
CV_SetValue(&cv_usejoystick, choice); CV_SetValue(&cv_usejoystick, choice);
#endif
} }
// ============= // =============

View file

@ -208,7 +208,6 @@ void M_QuitResponse(INT32 ch);
// Determines whether to show a level in the list (platter version does not need to be exposed) // Determines whether to show a level in the list (platter version does not need to be exposed)
boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt); boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt);
// flags for items in the menu // flags for items in the menu
// menu handle (what we do when key is pressed // menu handle (what we do when key is pressed
#define IT_TYPE 14 // (2+4+8) #define IT_TYPE 14 // (2+4+8)
@ -313,6 +312,10 @@ extern menu_t *currentMenu;
extern menu_t MainDef; extern menu_t MainDef;
extern menu_t SP_LoadDef; extern menu_t SP_LoadDef;
// Call upon joystick hotplug
void M_SetupJoystickMenu(INT32 choice);
extern menu_t OP_JoystickSetDef;
// Stuff for customizing the player select screen // Stuff for customizing the player select screen
typedef struct typedef struct
{ {

View file

@ -827,6 +827,23 @@ void I_JoyScale2(void)
JoyInfo2.scale = Joystick2.bGamepadStyle?1:cv_joyscale2.value; JoyInfo2.scale = Joystick2.bGamepadStyle?1:cv_joyscale2.value;
} }
// Cheat to get the device index for a joystick handle
INT32 I_GetJoystickDeviceIndex(SDL_Joystick *dev)
{
INT32 i, count = SDL_NumJoysticks();
for (i = 0; dev && i < count; i++)
{
SDL_Joystick *test = SDL_JoystickOpen(i);
if (test && test == dev)
return i;
else if (JoyInfo.dev != test && JoyInfo2.dev != test)
SDL_JoystickClose(test);
}
return -1;
}
/** \brief Joystick 1 buttons states /** \brief Joystick 1 buttons states
*/ */
static UINT64 lastjoybuttons = 0; static UINT64 lastjoybuttons = 0;
@ -842,7 +859,7 @@ static UINT64 lastjoyhats = 0;
*/ */
static void I_ShutdownJoystick(void) void I_ShutdownJoystick(void)
{ {
INT32 i; INT32 i;
event_t event; event_t event;
@ -876,14 +893,8 @@ static void I_ShutdownJoystick(void)
joystick_started = 0; joystick_started = 0;
JoyReset(&JoyInfo); JoyReset(&JoyInfo);
if (!joystick_started && !joystick2_started && SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK)
{ // don't shut down the subsystem here, because hotplugging
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
if (cv_usejoystick.value == 0)
{
I_OutputMsg("I_Joystick: SDL's Joystick system has been shutdown\n");
}
}
} }
void I_GetJoystickEvents(void) void I_GetJoystickEvents(void)
@ -1024,74 +1035,66 @@ void I_GetJoystickEvents(void)
*/ */
static int joy_open(const char *fname) static int joy_open(int joyindex)
{ {
int joyindex = atoi(fname); SDL_Joystick *newdev = NULL;
int num_joy = 0; int num_joy = 0;
int i;
if (joystick_started == 0 && joystick2_started == 0) if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
{ {
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) CONS_Printf(M_GetText("Joystick subsystem not started\n"));
{
CONS_Printf(M_GetText("Couldn't initialize gamepad: %s\n"), SDL_GetError());
return -1; return -1;
} }
else
{
num_joy = SDL_NumJoysticks();
}
if (num_joy < joyindex) if (joyindex <= 0)
{
CONS_Printf("Cannot use gamepad #%d/(%s), it doesn't exist\n",joyindex,fname);
for (i = 0; i < num_joy; i++)
CONS_Printf("#%d/(%s)\n", i+1, SDL_JoystickNameForIndex(i));
I_ShutdownJoystick();
return -1; return -1;
}
}
else
{
JoyReset(&JoyInfo);
//I_ShutdownJoystick();
//joy_open(fname);
}
num_joy = SDL_NumJoysticks(); num_joy = SDL_NumJoysticks();
if (joyindex <= 0 || num_joy == 0 || JoyInfo.oldjoy == joyindex) if (num_joy == 0)
{ {
// I_OutputMsg("Unable to use that joystick #(%s), non-number\n",fname);
if (num_joy != 0)
{
CONS_Printf(M_GetText("Found %d joysticks on this system\n"), num_joy);
for (i = 0; i < num_joy; i++)
CONS_Printf("#%d/(%s)\n", i+1, SDL_JoystickNameForIndex(i));
}
else
CONS_Printf("%s", M_GetText("Found no joysticks on this system\n")); CONS_Printf("%s", M_GetText("Found no joysticks on this system\n"));
if (joyindex <= 0 || num_joy == 0) return 0; return -1;
} }
JoyInfo.dev = SDL_JoystickOpen(joyindex-1); newdev = SDL_JoystickOpen(joyindex-1);
// Handle the edge case where the device <-> joystick index assignment can change due to hotplugging
// This indexing is SDL's responsibility and there's not much we can do about it.
//
// Example:
// 1. Plug Controller A -> Index 0 opened
// 2. Plug Controller B -> Index 1 opened
// 3. Unplug Controller A -> Index 0 closed, Index 1 active
// 4. Unplug Controller B -> Index 0 inactive, Index 1 closed
// 5. Plug Controller B -> Index 0 opened
// 6. Plug Controller A -> Index 0 REPLACED, opened as Controller A; Index 1 is now Controller B
if (JoyInfo.dev)
{
if (JoyInfo.dev == newdev // same device, nothing to do
|| (newdev == NULL && SDL_JoystickGetAttached(JoyInfo.dev))) // we failed, but already have a working device
return JoyInfo.axises;
// Else, we're changing devices, so send neutral joy events
CONS_Debug(DBG_GAMELOGIC, "Joystick1 device is changing; resetting events...\n");
I_ShutdownJoystick();
}
JoyInfo.dev = newdev;
if (JoyInfo.dev == NULL) if (JoyInfo.dev == NULL)
{ {
CONS_Printf(M_GetText("Couldn't open joystick: %s\n"), SDL_GetError()); CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick1: Couldn't open device - %s\n"), SDL_GetError());
I_ShutdownJoystick();
return -1; return -1;
} }
else else
{ {
CONS_Printf(M_GetText("Joystick: %s\n"), SDL_JoystickName(JoyInfo.dev)); CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick1: %s\n"), SDL_JoystickName(JoyInfo.dev));
JoyInfo.axises = SDL_JoystickNumAxes(JoyInfo.dev); JoyInfo.axises = SDL_JoystickNumAxes(JoyInfo.dev);
if (JoyInfo.axises > JOYAXISSET*2) if (JoyInfo.axises > JOYAXISSET*2)
JoyInfo.axises = JOYAXISSET*2; JoyInfo.axises = JOYAXISSET*2;
/* if (joyaxes<2) /* if (joyaxes<2)
{ {
I_OutputMsg("Not enought axes?\n"); I_OutputMsg("Not enought axes?\n");
I_ShutdownJoystick();
return 0; return 0;
}*/ }*/
@ -1126,7 +1129,7 @@ static UINT64 lastjoy2hats = 0;
\return void \return void
*/ */
static void I_ShutdownJoystick2(void) void I_ShutdownJoystick2(void)
{ {
INT32 i; INT32 i;
event_t event; event_t event;
@ -1160,14 +1163,8 @@ static void I_ShutdownJoystick2(void)
joystick2_started = 0; joystick2_started = 0;
JoyReset(&JoyInfo2); JoyReset(&JoyInfo2);
if (!joystick_started && !joystick2_started && SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK)
{ // don't shut down the subsystem here, because hotplugging
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
if (cv_usejoystick2.value == 0)
{
DEBFILE("I_Joystick2: SDL's Joystick system has been shutdown\n");
}
}
} }
void I_GetJoystick2Events(void) void I_GetJoystick2Events(void)
@ -1310,72 +1307,66 @@ void I_GetJoystick2Events(void)
*/ */
static int joy_open2(const char *fname) static int joy_open2(int joyindex)
{ {
int joyindex = atoi(fname); SDL_Joystick *newdev = NULL;
int num_joy = 0; int num_joy = 0;
int i;
if (joystick_started == 0 && joystick2_started == 0) if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
{ {
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) CONS_Printf(M_GetText("Joystick subsystem not started\n"));
{
CONS_Printf(M_GetText("Couldn't initialize gamepad: %s\n"), SDL_GetError());
return -1; return -1;
} }
else
num_joy = SDL_NumJoysticks();
if (num_joy < joyindex) if (joyindex <= 0)
{
CONS_Printf("Cannot use gamepad #%d/(%s), it doesn't exist\n",joyindex,fname);
for (i = 0; i < num_joy; i++)
CONS_Printf("#%d/(%s)\n", i+1, SDL_JoystickNameForIndex(i));
I_ShutdownJoystick2();
return -1; return -1;
}
}
else
{
JoyReset(&JoyInfo2);
//I_ShutdownJoystick();
//joy_open(fname);
}
num_joy = SDL_NumJoysticks(); num_joy = SDL_NumJoysticks();
if (joyindex <= 0 || num_joy == 0 || JoyInfo2.oldjoy == joyindex) if (num_joy == 0)
{ {
// I_OutputMsg("Unable to use that joystick #(%s), non-number\n",fname);
if (num_joy != 0)
{
CONS_Printf(M_GetText("Found %d joysticks on this system\n"), num_joy);
for (i = 0; i < num_joy; i++)
CONS_Printf("#%d/(%s)\n", i+1, SDL_JoystickNameForIndex(i));
}
else
CONS_Printf("%s", M_GetText("Found no joysticks on this system\n")); CONS_Printf("%s", M_GetText("Found no joysticks on this system\n"));
if (joyindex <= 0 || num_joy == 0) return 0; return -1;
} }
JoyInfo2.dev = SDL_JoystickOpen(joyindex-1); newdev = SDL_JoystickOpen(joyindex-1);
if (!JoyInfo2.dev) // Handle the edge case where the device <-> joystick index assignment can change due to hotplugging
// This indexing is SDL's responsibility and there's not much we can do about it.
//
// Example:
// 1. Plug Controller A -> Index 0 opened
// 2. Plug Controller B -> Index 1 opened
// 3. Unplug Controller A -> Index 0 closed, Index 1 active
// 4. Unplug Controller B -> Index 0 inactive, Index 1 closed
// 5. Plug Controller B -> Index 0 opened
// 6. Plug Controller A -> Index 0 REPLACED, opened as Controller A; Index 1 is now Controller B
if (JoyInfo2.dev)
{ {
CONS_Printf(M_GetText("Couldn't open joystick2: %s\n"), SDL_GetError()); if (JoyInfo2.dev == newdev // same device, nothing to do
|| (newdev == NULL && SDL_JoystickGetAttached(JoyInfo2.dev))) // we failed, but already have a working device
return JoyInfo.axises;
// Else, we're changing devices, so send neutral joy events
CONS_Debug(DBG_GAMELOGIC, "Joystick2 device is changing; resetting events...\n");
I_ShutdownJoystick2(); I_ShutdownJoystick2();
}
JoyInfo2.dev = newdev;
if (JoyInfo2.dev == NULL)
{
CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick2: couldn't open device - %s\n"), SDL_GetError());
return -1; return -1;
} }
else else
{ {
CONS_Printf(M_GetText("Joystick2: %s\n"), SDL_JoystickName(JoyInfo2.dev)); CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick2: %s\n"), SDL_JoystickName(JoyInfo2.dev));
JoyInfo2.axises = SDL_JoystickNumAxes(JoyInfo2.dev); JoyInfo2.axises = SDL_JoystickNumAxes(JoyInfo2.dev);
if (JoyInfo2.axises > JOYAXISSET*2) if (JoyInfo2.axises > JOYAXISSET*2)
JoyInfo2.axises = JOYAXISSET*2; JoyInfo2.axises = JOYAXISSET*2;
/* if (joyaxes < 2) /* if (joyaxes<2)
{ {
I_OutputMsg("Not enought axes?\n"); I_OutputMsg("Not enought axes?\n");
I_ShutdownJoystick2();
return 0; return 0;
}*/ }*/
@ -1400,7 +1391,11 @@ static int joy_open2(const char *fname)
// //
void I_InitJoystick(void) void I_InitJoystick(void)
{ {
I_ShutdownJoystick(); SDL_Joystick *newjoy = NULL;
//I_ShutdownJoystick();
if (M_CheckParm("-nojoy"))
return;
if (M_CheckParm("-noxinput")) if (M_CheckParm("-noxinput"))
SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE); SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE);
@ -1408,21 +1403,48 @@ void I_InitJoystick(void)
if (M_CheckParm("-nohidapi")) if (M_CheckParm("-nohidapi"))
SDL_SetHintWithPriority("SDL_JOYSTICK_HIDAPI", "0", SDL_HINT_OVERRIDE); SDL_SetHintWithPriority("SDL_JOYSTICK_HIDAPI", "0", SDL_HINT_OVERRIDE);
if (!strcmp(cv_usejoystick.string, "0") || M_CheckParm("-nojoy")) if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
return;
if (joy_open(cv_usejoystick.string) != -1)
JoyInfo.oldjoy = atoi(cv_usejoystick.string);
else
{ {
cv_usejoystick.value = 0; CONS_Printf("I_InitJoystick()...\n");
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1)
{
CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError());
return; return;
} }
}
if (cv_usejoystick.value)
newjoy = SDL_JoystickOpen(cv_usejoystick.value-1);
if (newjoy && JoyInfo2.dev == newjoy) // don't override an active device
cv_usejoystick.value = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1;
else if (newjoy && joy_open(cv_usejoystick.value) != -1)
{
// SDL's device indexes are unstable, so cv_usejoystick may not match
// the actual device index. So let's cheat a bit and find the device's current index.
JoyInfo.oldjoy = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1;
joystick_started = 1; joystick_started = 1;
}
else
{
if (JoyInfo.oldjoy)
I_ShutdownJoystick();
cv_usejoystick.value = 0;
joystick_started = 0;
}
if (JoyInfo.dev != newjoy && JoyInfo2.dev != newjoy)
SDL_JoystickClose(newjoy);
} }
void I_InitJoystick2(void) void I_InitJoystick2(void)
{ {
I_ShutdownJoystick2(); SDL_Joystick *newjoy = NULL;
//I_ShutdownJoystick2();
if (M_CheckParm("-nojoy"))
return;
if (M_CheckParm("-noxinput")) if (M_CheckParm("-noxinput"))
SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE); SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE);
@ -1430,64 +1452,77 @@ void I_InitJoystick2(void)
if (M_CheckParm("-nohidapi")) if (M_CheckParm("-nohidapi"))
SDL_SetHintWithPriority("SDL_JOYSTICK_HIDAPI", "0", SDL_HINT_OVERRIDE); SDL_SetHintWithPriority("SDL_JOYSTICK_HIDAPI", "0", SDL_HINT_OVERRIDE);
if (!strcmp(cv_usejoystick2.string, "0") || M_CheckParm("-nojoy")) if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
return;
if (joy_open2(cv_usejoystick2.string) != -1)
JoyInfo2.oldjoy = atoi(cv_usejoystick2.string);
else
{ {
cv_usejoystick2.value = 0; CONS_Printf("I_InitJoystick2()...\n");
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1)
{
CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError());
return; return;
} }
}
if (cv_usejoystick2.value)
newjoy = SDL_JoystickOpen(cv_usejoystick2.value-1);
if (newjoy && JoyInfo.dev == newjoy) // don't override an active device
cv_usejoystick2.value = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1;
else if (newjoy && joy_open2(cv_usejoystick2.value) != -1)
{
// SDL's device indexes are unstable, so cv_usejoystick may not match
// the actual device index. So let's cheat a bit and find the device's current index.
JoyInfo2.oldjoy = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1;
joystick2_started = 1; joystick2_started = 1;
}
else
{
if (JoyInfo2.oldjoy)
I_ShutdownJoystick2();
cv_usejoystick2.value = 0;
joystick2_started = 0;
}
if (JoyInfo.dev != newjoy && JoyInfo2.dev != newjoy)
SDL_JoystickClose(newjoy);
} }
static void I_ShutdownInput(void) static void I_ShutdownInput(void)
{ {
// Yes, the name is misleading: these send neutral events to
// clean up the unplugged joystick's input
// Note these methods are internal to this file, not called elsewhere.
I_ShutdownJoystick();
I_ShutdownJoystick2();
if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK) if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK)
{ {
JoyReset(&JoyInfo); CONS_Printf("Shutting down joy system\n");
JoyReset(&JoyInfo2);
SDL_QuitSubSystem(SDL_INIT_JOYSTICK); SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
I_OutputMsg("I_Joystick: SDL's Joystick system has been shutdown\n");
} }
} }
INT32 I_NumJoys(void) INT32 I_NumJoys(void)
{ {
INT32 numjoy = 0; INT32 numjoy = 0;
if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK)
{
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) != -1)
numjoy = SDL_NumJoysticks();
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
}
else
numjoy = SDL_NumJoysticks(); numjoy = SDL_NumJoysticks();
return numjoy; return numjoy;
} }
static char joyname[255]; // MAX_PATH; joystick name is straight from the driver static char joyname[255]; // joystick name is straight from the driver
const char *I_GetJoyName(INT32 joyindex) const char *I_GetJoyName(INT32 joyindex)
{ {
const char *tempname = NULL; const char *tempname = NULL;
joyname[0] = 0;
joyindex--; //SDL's Joystick System starts at 0, not 1 joyindex--; //SDL's Joystick System starts at 0, not 1
if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK)
{
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) != -1)
{ {
tempname = SDL_JoystickNameForIndex(joyindex); tempname = SDL_JoystickNameForIndex(joyindex);
if (tempname) if (tempname)
strncpy(joyname, tempname, 254); strncpy(joyname, tempname, 255);
}
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
}
else
{
tempname = SDL_JoystickNameForIndex(joyindex);
if (tempname)
strncpy(joyname, tempname, 254);
} }
return joyname; return joyname;
} }

View file

@ -886,6 +886,136 @@ void I_GetEvent(void)
case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONDOWN:
Impl_HandleJoystickButtonEvent(evt.jbutton, evt.type); Impl_HandleJoystickButtonEvent(evt.jbutton, evt.type);
break; break;
case SDL_JOYDEVICEADDED:
{
SDL_Joystick *newjoy = SDL_JoystickOpen(evt.jdevice.which);
CONS_Debug(DBG_GAMELOGIC, "Joystick device index %d added\n", evt.jdevice.which + 1);
// Because SDL's device index is unstable, we're going to cheat here a bit:
// For the first joystick setting that is NOT active:
// 1. Set cv_usejoystickX.value to the new device index (this does not change what is written to config.cfg)
// 2. Set OTHERS' cv_usejoystickX.value to THEIR new device index, because it likely changed
// * If device doesn't exist, switch cv_usejoystick back to default value (.string)
// * BUT: If that default index is being occupied, use ANOTHER cv_usejoystick's default value!
if (newjoy && (!JoyInfo.dev || !SDL_JoystickGetAttached(JoyInfo.dev))
&& JoyInfo2.dev != newjoy) // don't override a currently active device
{
cv_usejoystick.value = evt.jdevice.which + 1;
if (JoyInfo2.dev)
cv_usejoystick2.value = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1;
else if (atoi(cv_usejoystick2.string) != JoyInfo.oldjoy
&& atoi(cv_usejoystick2.string) != cv_usejoystick.value)
cv_usejoystick2.value = atoi(cv_usejoystick2.string);
else if (atoi(cv_usejoystick.string) != JoyInfo.oldjoy
&& atoi(cv_usejoystick.string) != cv_usejoystick.value)
cv_usejoystick2.value = atoi(cv_usejoystick.string);
else // we tried...
cv_usejoystick2.value = 0;
}
else if (newjoy && (!JoyInfo2.dev || !SDL_JoystickGetAttached(JoyInfo2.dev))
&& JoyInfo.dev != newjoy) // don't override a currently active device
{
cv_usejoystick2.value = evt.jdevice.which + 1;
if (JoyInfo.dev)
cv_usejoystick.value = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1;
else if (atoi(cv_usejoystick.string) != JoyInfo2.oldjoy
&& atoi(cv_usejoystick.string) != cv_usejoystick2.value)
cv_usejoystick.value = atoi(cv_usejoystick.string);
else if (atoi(cv_usejoystick2.string) != JoyInfo2.oldjoy
&& atoi(cv_usejoystick2.string) != cv_usejoystick2.value)
cv_usejoystick.value = atoi(cv_usejoystick2.string);
else // we tried...
cv_usejoystick.value = 0;
}
// Was cv_usejoystick disabled in settings?
if (!strcmp(cv_usejoystick.string, "0") || !cv_usejoystick.value)
cv_usejoystick.value = 0;
else if (atoi(cv_usejoystick.string) <= I_NumJoys() // don't mess if we intentionally set higher than NumJoys
&& cv_usejoystick.value) // update the cvar ONLY if a device exists
CV_SetValue(&cv_usejoystick, cv_usejoystick.value);
if (!strcmp(cv_usejoystick2.string, "0") || !cv_usejoystick2.value)
cv_usejoystick2.value = 0;
else if (atoi(cv_usejoystick2.string) <= I_NumJoys() // don't mess if we intentionally set higher than NumJoys
&& cv_usejoystick2.value) // update the cvar ONLY if a device exists
CV_SetValue(&cv_usejoystick2, cv_usejoystick2.value);
// Update all joysticks' init states
// This is a little wasteful since cv_usejoystick already calls this, but
// we need to do this in case CV_SetValue did nothing because the string was already same.
// if the device is already active, this should do nothing, effectively.
I_InitJoystick();
I_InitJoystick2();
CONS_Debug(DBG_GAMELOGIC, "Joystick1 device index: %d\n", JoyInfo.oldjoy);
CONS_Debug(DBG_GAMELOGIC, "Joystick2 device index: %d\n", JoyInfo2.oldjoy);
// update the menu
if (currentMenu == &OP_JoystickSetDef)
M_SetupJoystickMenu(0);
if (JoyInfo.dev != newjoy && JoyInfo2.dev != newjoy)
SDL_JoystickClose(newjoy);
}
break;
case SDL_JOYDEVICEREMOVED:
if (JoyInfo.dev && !SDL_JoystickGetAttached(JoyInfo.dev))
{
CONS_Debug(DBG_GAMELOGIC, "Joystick1 removed, device index: %d\n", JoyInfo.oldjoy);
I_ShutdownJoystick();
}
if (JoyInfo2.dev && !SDL_JoystickGetAttached(JoyInfo2.dev))
{
CONS_Debug(DBG_GAMELOGIC, "Joystick2 removed, device index: %d\n", JoyInfo2.oldjoy);
I_ShutdownJoystick2();
}
// Update the device indexes, because they likely changed
// * If device doesn't exist, switch cv_usejoystick back to default value (.string)
// * BUT: If that default index is being occupied, use ANOTHER cv_usejoystick's default value!
if (JoyInfo.dev)
cv_usejoystick.value = JoyInfo.oldjoy = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1;
else if (atoi(cv_usejoystick.string) != JoyInfo2.oldjoy)
cv_usejoystick.value = atoi(cv_usejoystick.string);
else if (atoi(cv_usejoystick2.string) != JoyInfo2.oldjoy)
cv_usejoystick.value = atoi(cv_usejoystick2.string);
else // we tried...
cv_usejoystick.value = 0;
if (JoyInfo2.dev)
cv_usejoystick2.value = JoyInfo2.oldjoy = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1;
else if (atoi(cv_usejoystick2.string) != JoyInfo.oldjoy)
cv_usejoystick2.value = atoi(cv_usejoystick2.string);
else if (atoi(cv_usejoystick.string) != JoyInfo.oldjoy)
cv_usejoystick2.value = atoi(cv_usejoystick.string);
else // we tried...
cv_usejoystick2.value = 0;
// Was cv_usejoystick disabled in settings?
if (!strcmp(cv_usejoystick.string, "0"))
cv_usejoystick.value = 0;
else if (atoi(cv_usejoystick.string) <= I_NumJoys() // don't mess if we intentionally set higher than NumJoys
&& cv_usejoystick.value) // update the cvar ONLY if a device exists
CV_SetValue(&cv_usejoystick, cv_usejoystick.value);
if (!strcmp(cv_usejoystick2.string, "0"))
cv_usejoystick2.value = 0;
else if (atoi(cv_usejoystick2.string) <= I_NumJoys() // don't mess if we intentionally set higher than NumJoys
&& cv_usejoystick2.value) // update the cvar ONLY if a device exists
CV_SetValue(&cv_usejoystick2, cv_usejoystick2.value);
CONS_Debug(DBG_GAMELOGIC, "Joystick1 device index: %d\n", JoyInfo.oldjoy);
CONS_Debug(DBG_GAMELOGIC, "Joystick2 device index: %d\n", JoyInfo2.oldjoy);
// update the menu
if (currentMenu == &OP_JoystickSetDef)
M_SetupJoystickMenu(0);
break;
case SDL_QUIT: case SDL_QUIT:
I_Quit(); I_Quit();
M_QuitResponse('y'); M_QuitResponse('y');

View file

@ -31,6 +31,9 @@ extern SDL_bool framebuffer;
#define SDL2STUB() CONS_Printf("SDL2: stubbed: %s:%d\n", __func__, __LINE__) #define SDL2STUB() CONS_Printf("SDL2: stubbed: %s:%d\n", __func__, __LINE__)
#endif #endif
// So m_menu knows whether to store cv_usejoystick value or string
#define JOYSTICK_HOTPLUG
/** \brief The JoyInfo_s struct /** \brief The JoyInfo_s struct
info about joystick info about joystick
@ -67,6 +70,13 @@ extern SDLJoyInfo_t JoyInfo;
*/ */
extern SDLJoyInfo_t JoyInfo2; extern SDLJoyInfo_t JoyInfo2;
// So we can call this from i_video event loop
void I_ShutdownJoystick(void);
void I_ShutdownJoystick2(void);
// Cheat to get the device index for a joystick handle
INT32 I_GetJoystickDeviceIndex(SDL_Joystick *dev);
void I_GetConsoleEvents(void); void I_GetConsoleEvents(void);
void SDLforceUngrabMouse(void); void SDLforceUngrabMouse(void);