Handle gamepads from interface dynamically

Fixes numerous issues with hotswapping, gamepad assignment, and menu
responsiveness.
This commit is contained in:
Eidolon 2023-03-05 00:05:22 -06:00
parent feb70916c1
commit 86a9579e16
18 changed files with 558 additions and 597 deletions

View file

@ -449,7 +449,7 @@ boolean AM_Responder(event_t *ev)
{
//faB: prevent alt-tab in win32 version to activate automap just before
// minimizing the app; doesn't do any harm to the DOS version
if (!gamekeydown[0][KEY_LALT] && !gamekeydown[0][KEY_RALT])
if (!G_GetDeviceGameKeyDownArray(0)[KEY_LALT] && !G_GetDeviceGameKeyDownArray(0)[KEY_RALT])
{
bigstate = 0; //added : 24-01-98 : toggle off large view
AM_Start();
@ -1069,7 +1069,7 @@ static void AM_drawWalls(UINT8 pass)
else if (backc1 != frontc1 || backc2 != frontc2)
{
if (!(pass & PASS_INTANGIBLE))
;
;
else if (abs(backc1 - frontc1) < maxstep
|| abs(backc2 - frontc2) < maxstep)
{
@ -1115,7 +1115,7 @@ static void AM_drawWalls(UINT8 pass)
else
{
ffloor_t *rover = NULL;
if (lines[i].frontsector->ffloors || lines[i].backsector->ffloors)
{
if (lines[i].backsector->ffloors == NULL)

View file

@ -1485,7 +1485,7 @@ void CL_UpdateServerList (void)
static void M_ConfirmConnect(void)
{
if (G_PlayerInputDown(0, gc_a, 1) || gamekeydown[0][KEY_ENTER])
if (G_PlayerInputDown(0, gc_a, 1) || G_GetDeviceGameKeyDownArray(0)[KEY_ENTER])
{
if (totalfilesrequestednum > 0)
{
@ -1512,7 +1512,7 @@ static void M_ConfirmConnect(void)
M_StopMessage(0);
}
else if (G_PlayerInputDown(0, gc_b, 1) || G_PlayerInputDown(0, gc_x, 1) || gamekeydown[0][KEY_ESCAPE])
else if (G_PlayerInputDown(0, gc_b, 1) || G_PlayerInputDown(0, gc_x, 1) || G_GetDeviceGameKeyDownArray(0)[KEY_ESCAPE])
{
cl_mode = CL_ABORTED;
M_StopMessage(0);
@ -1962,7 +1962,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
renderdeltatics = FRACUNIT;
rendertimefrac = FRACUNIT;
memset(deviceResponding, false, sizeof (deviceResponding));
G_ResetAllDeviceResponding();
if (netgame)
{
@ -1979,7 +1979,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
{
if (G_PlayerInputDown(0, gc_b, 1)
|| G_PlayerInputDown(0, gc_x, 1)
|| gamekeydown[0][KEY_ESCAPE])
|| G_GetDeviceGameKeyDownArray(0)[KEY_ESCAPE])
cl_mode = CL_ABORTED;
}
}

View file

@ -28,7 +28,9 @@ typedef enum
ev_keyup,
ev_console,
ev_mouse,
ev_joystick,
ev_gamepad_axis,
ev_gamepad_device_added,
ev_gamepad_device_removed,
} evtype_t;
// Event structure.
@ -38,7 +40,7 @@ struct event_t
INT32 data1; // keys / mouse/joystick buttons
INT32 data2; // mouse/joystick x move
INT32 data3; // mouse/joystick y move
INT32 device; // which player's device it belongs to
INT32 device; // which device ID it belongs to (controller ID)
};
//

View file

@ -171,6 +171,54 @@ UINT8 ctrldown = 0; // 0x1 left, 0x2 right
UINT8 altdown = 0; // 0x1 left, 0x2 right
boolean capslock = 0; // gee i wonder what this does.
static void HandleGamepadDeviceAdded(event_t *ev)
{
I_Assert(ev != NULL);
I_Assert(ev->type == ev_gamepad_device_added);
G_RegisterAvailableGamepad(ev->device);
CONS_Debug(DBG_GAMELOGIC, "Registered available gamepad device %d\n", ev->device);
}
static void HandleGamepadDeviceRemoved(event_t *ev)
{
int i = 0;
I_Assert(ev != NULL);
I_Assert(ev->type == ev_gamepad_device_removed);
G_UnregisterAvailableGamepad(ev->device);
CONS_Debug(DBG_GAMELOGIC, "Unregistered available gamepad device %d\n", ev->device);
// Downstream responders need to update player gamepad assignments, pause, etc
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
INT32 device = G_GetDeviceForPlayer(i);
if (device == ev->device)
{
G_SetDeviceForPlayer(i, -1);
}
}
}
/// Respond to added/removed device events, for bookkeeping available gamepads.
static void HandleGamepadDeviceEvents(event_t *ev)
{
I_Assert(ev != NULL);
switch (ev->type)
{
case ev_gamepad_device_added:
HandleGamepadDeviceAdded(ev);
break;
case ev_gamepad_device_removed:
HandleGamepadDeviceRemoved(ev);
break;
default:
break;
}
}
//
// D_ProcessEvents
// Send all the events of the given timestamp down the responder chain
@ -183,11 +231,13 @@ void D_ProcessEvents(void)
boolean eaten;
boolean menuresponse = false;
memset(deviceResponding, false, sizeof (deviceResponding));
G_ResetAllDeviceResponding();
for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1))
{
ev = &events[eventtail];
HandleGamepadDeviceEvents(ev);
// Screenshots over everything so that they can be taken anywhere.
if (M_ScreenshotResponder(ev))
continue; // ate the event
@ -976,7 +1026,7 @@ void D_ClearState(void)
// clear cmd building stuff
memset(gamekeydown, 0, sizeof (gamekeydown));
memset(deviceResponding, false, sizeof (deviceResponding));
G_ResetAllDeviceResponding();
// Reset the palette
if (rendermode != render_none)
@ -1507,6 +1557,8 @@ void D_SRB2Main(void)
CONS_Printf("I_StartupGraphics()...\n");
I_StartupGraphics();
I_StartupInput();
if (rendermode != render_none)
{
I_NewTwodeeFrame();

View file

@ -237,9 +237,6 @@ static CV_PossibleValue_t usemouse_cons_t[] = {{0, "Off"}, {1, "On"}, {2, "Force
#ifdef LJOYSTICK
static CV_PossibleValue_t joyport_cons_t[] = {{1, "/dev/js0"}, {2, "/dev/js1"}, {3, "/dev/js2"},
{4, "/dev/js3"}, {0, NULL}};
#else
// accept whatever value - it is in fact the joystick device number
static CV_PossibleValue_t usejoystick_cons_t[] = {{-1, "MIN"}, {MAXGAMEPADS, "MAX"}, {0, NULL}};
#endif
static CV_PossibleValue_t teamscramble_cons_t[] = {{0, "Off"}, {1, "Random"}, {2, "Points"}, {0, NULL}};
@ -332,13 +329,6 @@ consvar_t cv_skipmapcheck = CVAR_INIT ("skipmapcheck", "Off", CV_SAVE, CV_OnOff,
consvar_t cv_usemouse = CVAR_INIT ("use_mouse", "Off", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse);
consvar_t cv_usejoystick[MAXSPLITSCREENPLAYERS] = {
CVAR_INIT ("use_device", "1", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick1),
CVAR_INIT ("use_device2", "2", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick2),
CVAR_INIT ("use_device3", "3", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick3),
CVAR_INIT ("use_device4", "4", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick4)
};
#if (defined (LJOYSTICK) || defined (HAVE_SDL))
consvar_t cv_joyscale[MAXSPLITSCREENPLAYERS] = {
CVAR_INIT ("padscale", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale),
@ -1039,7 +1029,6 @@ void D_RegisterClientCommands(void)
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
CV_RegisterVar(&cv_usejoystick[i]);
CV_RegisterVar(&cv_joyscale[i]);
#ifdef LJOYSTICK
CV_RegisterVar(&cv_joyport[i]);

View file

@ -47,7 +47,6 @@ extern consvar_t cv_splitplayers;
extern consvar_t cv_seenames;
extern consvar_t cv_usemouse;
extern consvar_t cv_usejoystick[MAXSPLITSCREENPLAYERS];
extern consvar_t cv_joyscale[MAXSPLITSCREENPLAYERS];
#ifdef LJOYSTICK
extern consvar_t cv_joyport[MAXSPLITSCREENPLAYERS];

View file

@ -513,7 +513,7 @@ void G_UpdateTimeStickerMedals(UINT16 map, boolean showownrecord)
break;
}
case ET_MAP:
{
{
if (emblem->flags & ME_SPBATTACK && cv_dummyspbattack.value)
break;
goto bademblem;
@ -884,6 +884,7 @@ static INT32 keyboardMenuDefaults[][2] = {
INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, UINT8 menuPlayers)
{
INT32 deviceID;
INT32 avail_gamepad_id = 0;
INT32 i, j;
INT32 deadzone = 0;
boolean trydefaults = true;
@ -900,7 +901,22 @@ INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, UINT8 menuPlayers)
deadzone = (JOYAXISRANGE * cv_deadzone[p].value) / FRACUNIT;
deviceID = cv_usejoystick[p].value;
deviceID = G_GetDeviceForPlayer(p);
if (deviceID == -1)
{
INT32 keyboard_player = G_GetPlayerForDevice(KEYBOARD_MOUSE_DEVICE);
// Player 1 is always allowed to use the keyboard in 1P (there is a check for splitscreen later in this func)
if (p == KEYBOARD_MOUSE_DEVICE && keyboard_player == -1)
{
deviceID = KEYBOARD_MOUSE_DEVICE;
}
else
{
goto deviceunassigned;
}
}
retrygetcontrol:
for (i = 0; i < MAXINPUTMAPPING; i++)
@ -910,7 +926,6 @@ retrygetcontrol:
INT32 value = 0;
boolean processinput = true;
// for menus, keyboards have defaults!
if (deviceID == 0)
{
@ -951,9 +966,9 @@ retrygetcontrol:
// It's possible to access this control right now, so let's disable the default control backup for later.
trydefaults = false;
value = gamekeydown[deviceID][key];
if (menukey && gamekeydown[deviceID][menukey])
value = gamekeydown[deviceID][menukey];
value = G_GetDeviceGameKeyDownArray(deviceID)[key];
if (menukey && G_GetDeviceGameKeyDownArray(deviceID)[menukey])
value = G_GetDeviceGameKeyDownArray(deviceID)[menukey];
if (value >= deadzone)
{
@ -962,11 +977,13 @@ retrygetcontrol:
}
}
deviceunassigned:
// If you're on controller, try your keyboard-based binds as an immediate backup.
// Do not do this if there are more than 1 local player.
if (p == 0 && deviceID > 0 && !tryingotherID && menuPlayers < 2 && !splitscreen)
{
deviceID = 0;
deviceID = KEYBOARD_MOUSE_DEVICE;
goto retrygetcontrol;
}
@ -981,16 +998,21 @@ retrygetcontrol:
if (!tryingotherID)
{
deviceID = MAXDEVICES;
avail_gamepad_id = 0;
tryingotherID = true;
}
loweringid:
deviceID--;
if (avail_gamepad_id >= G_GetNumAvailableGamepads())
{
return 0;
}
deviceID = G_GetAvailableGamepadDevice(avail_gamepad_id);
avail_gamepad_id += 1;
if (deviceID > 0)
{
for (i = 0; i < menuPlayers; i++)
{
if (deviceID != cv_usejoystick[i].value)
if (deviceID != G_GetDeviceForPlayer(i))
continue;
// Controller taken? Try again...
goto loweringid;
@ -1005,7 +1027,7 @@ loweringid:
trydefaults = false;
controltable = &(gamecontroldefault[gc][0]);
tryingotherID = false;
deviceID = cv_usejoystick[p].value;
deviceID = G_GetDeviceForPlayer(p);
goto retrygetcontrol;
}
@ -1530,7 +1552,7 @@ void G_DoLoadLevel(boolean resetplayer)
// clear cmd building stuff
memset(gamekeydown, 0, sizeof (gamekeydown));
memset(deviceResponding, false, sizeof (deviceResponding));
G_ResetAllDeviceResponding();
// clear hud messages remains (usually from game startup)
CON_ClearHUD();
@ -1828,7 +1850,7 @@ boolean G_Responder(event_t *ev)
case ev_mouse:
return true; // eat events
case ev_joystick:
case ev_gamepad_axis:
return true; // eat events
default:

View file

@ -19,6 +19,7 @@
#include "d_net.h"
#include "console.h"
#include "i_joy.h" // JOYAXISRANGE
#include "z_zone.h"
#define MAXMOUSESENSITIVITY 100 // sensitivity steps
@ -35,7 +36,6 @@ consvar_t cv_controlperkey = CVAR_INIT ("controlperkey", "One", CV_SAVE, onecont
// current state of the keys
// FRACUNIT for fully pressed, 0 for not pressed
INT32 gamekeydown[MAXDEVICES][NUMINPUTS];
boolean deviceResponding[MAXDEVICES];
// two key codes (or virtual key) per game control
INT32 gamecontrol[MAXSPLITSCREENPLAYERS][num_gamecontrols][MAXINPUTMAPPING];
@ -68,13 +68,82 @@ const INT32 gcl_full[num_gcl_full] = {
};
*/
INT32 G_GetDevicePlayer(INT32 deviceID)
static INT32 g_gamekeydown_device0[NUMINPUTS];
static INT32 g_available_gamepad_devices;
static INT32 g_gamepad_device_ids[MAXGAMEPADS];
static INT32* g_gamepad_gamekeydown[MAXGAMEPADS];
static boolean g_device0_responding;
static boolean g_gamepad_responding[MAXGAMEPADS];
static INT32 g_player_devices[MAXSPLITSCREENPLAYERS] = {-1, -1, -1, -1};
void G_RegisterAvailableGamepad(INT32 device_id)
{
I_Assert(device_id >= 1);
if (g_available_gamepad_devices == MAXGAMEPADS)
{
// too many!
return;
}
g_gamepad_device_ids[g_available_gamepad_devices] = device_id;
g_gamepad_gamekeydown[g_available_gamepad_devices] = Z_CallocAlign(NUMINPUTS * sizeof(INT32), PU_STATIC, NULL, 4);
g_gamepad_responding[g_available_gamepad_devices] = false;
g_available_gamepad_devices += 1;
}
void G_UnregisterAvailableGamepad(INT32 device_id)
{
int i = 0;
I_Assert(device_id >= 1);
if (g_available_gamepad_devices <= 0)
{
return;
}
for (i = 0; i < g_available_gamepad_devices; i++)
{
if (g_gamepad_device_ids[i] == device_id)
{
int32_t *old_gamekeydown = g_gamepad_gamekeydown[i];
g_gamepad_device_ids[i] = g_gamepad_device_ids[g_available_gamepad_devices - 1];
g_gamepad_gamekeydown[i] = g_gamepad_gamekeydown[g_available_gamepad_devices - 1];
g_gamepad_responding[i] = g_gamepad_responding[g_available_gamepad_devices - 1];
Z_Free(old_gamekeydown);
g_available_gamepad_devices -= 1;
return;
}
}
}
INT32 G_GetNumAvailableGamepads(void)
{
return g_available_gamepad_devices;
}
INT32 G_GetAvailableGamepadDevice(INT32 available_index)
{
if (available_index < 0 || available_index >= G_GetNumAvailableGamepads())
{
return -1;
}
return g_gamepad_device_ids[available_index];
}
INT32 G_GetPlayerForDevice(INT32 device_id)
{
INT32 i;
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
if (deviceID == cv_usejoystick[i].value)
if (device_id == g_player_devices[i])
{
return i;
}
@ -83,6 +152,172 @@ INT32 G_GetDevicePlayer(INT32 deviceID)
return -1;
}
INT32 G_GetDeviceForPlayer(INT32 player)
{
int i;
if (G_GetPlayerForDevice(KEYBOARD_MOUSE_DEVICE) == player)
{
return KEYBOARD_MOUSE_DEVICE;
}
for (i = 0; i < G_GetNumAvailableGamepads() + 1; i++)
{
INT32 device = G_GetAvailableGamepadDevice(i);
if (G_GetPlayerForDevice(device) == player)
{
return device;
}
}
return -1;
}
void G_SetDeviceForPlayer(INT32 player, INT32 device)
{
int i;
I_Assert(player >= 0 && player < MAXSPLITSCREENPLAYERS);
I_Assert(device >= -1);
g_player_devices[player] = device;
if (device == -1)
{
return;
}
if (device != KEYBOARD_MOUSE_DEVICE)
{
I_SetGamepadPlayerIndex(device, player);
}
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
if (i == player)
{
continue;
}
if (g_player_devices[i] == device)
{
g_player_devices[i] = -1;
if (device > 0)
{
I_SetGamepadPlayerIndex(device, -1);
}
}
}
}
INT32* G_GetDeviceGameKeyDownArray(INT32 device)
{
int i;
I_Assert(device >= 0);
if (device == KEYBOARD_MOUSE_DEVICE)
{
return g_gamekeydown_device0;
}
for (i = 0; i < g_available_gamepad_devices; i++)
{
if (g_gamepad_device_ids[i] == device)
{
return g_gamepad_gamekeydown[i];
}
}
return NULL;
}
boolean G_IsDeviceResponding(INT32 device)
{
int i;
I_Assert(device >= 0);
if (device == KEYBOARD_MOUSE_DEVICE)
{
return g_device0_responding;
}
for (i = 0; i < g_available_gamepad_devices; i++)
{
INT32 device_id = G_GetAvailableGamepadDevice(i);
if (device_id == device)
{
return g_gamepad_responding[i];
}
}
return false;
}
void G_SetDeviceResponding(INT32 device, boolean responding)
{
int i;
I_Assert(device >= 0);
if (device == KEYBOARD_MOUSE_DEVICE)
{
g_device0_responding = responding;
return;
}
for (i = 0; i < g_available_gamepad_devices; i++)
{
INT32 device_id = G_GetAvailableGamepadDevice(i);
if (device_id == device)
{
g_gamepad_responding[i] = responding;
return;
}
}
}
void G_ResetAllDeviceResponding(void)
{
int i;
int num_gamepads;
g_device0_responding = false;
num_gamepads = G_GetNumAvailableGamepads();
for (i = 0; i < num_gamepads; i++)
{
g_gamepad_responding[i] = false;
}
}
static boolean AutomaticControllerReassignmentIsAllowed(INT32 device)
{
boolean device_is_gamepad = device > 0;
boolean device_is_unassigned = G_GetPlayerForDevice(device) == -1;
boolean gamestate_is_in_level = gamestate == GS_LEVEL;
return device_is_gamepad && device_is_unassigned && gamestate_is_in_level;
}
static INT32 AssignDeviceToFirstUnassignedPlayer(INT32 device)
{
int i;
for (i = 0; i < splitscreen + 1; i++)
{
if (G_GetDeviceForPlayer(i) == -1)
{
G_SetDeviceForPlayer(i, device);
return i;
}
}
return -1;
}
//
// Remaps the inputs to game controls.
//
@ -94,15 +329,15 @@ void G_MapEventsToControls(event_t *ev)
{
INT32 i;
if (ev->device >= 0 && ev->device < MAXDEVICES)
if (ev->device >= 0)
{
switch (ev->type)
{
case ev_keydown:
//case ev_keyup:
//case ev_mouse:
//case ev_joystick:
deviceResponding[ev->device] = true;
//case ev_gamepad_axis:
G_SetDeviceResponding(ev->device, true);
break;
default:
@ -119,7 +354,16 @@ void G_MapEventsToControls(event_t *ev)
case ev_keydown:
if (ev->data1 < NUMINPUTS)
{
gamekeydown[ev->device][ev->data1] = JOYAXISRANGE;
G_GetDeviceGameKeyDownArray(ev->device)[ev->data1] = JOYAXISRANGE;
if (AutomaticControllerReassignmentIsAllowed(ev->device))
{
INT32 assigned = AssignDeviceToFirstUnassignedPlayer(ev->device);
if (assigned >= 0)
{
CONS_Alert(CONS_NOTICE, "Player %d device was reassigned\n", assigned + 1);
}
}
}
#ifdef PARANOIA
else
@ -132,7 +376,7 @@ void G_MapEventsToControls(event_t *ev)
case ev_keyup:
if (ev->data1 < NUMINPUTS)
{
gamekeydown[ev->device][ev->data1] = 0;
G_GetDeviceGameKeyDownArray(ev->device)[ev->data1] = 0;
}
#ifdef PARANOIA
else
@ -147,32 +391,32 @@ void G_MapEventsToControls(event_t *ev)
if (ev->data2 < 0)
{
// Left
gamekeydown[ev->device][KEY_MOUSEMOVE + 2] = abs(ev->data2);
gamekeydown[ev->device][KEY_MOUSEMOVE + 3] = 0;
G_GetDeviceGameKeyDownArray(ev->device)[KEY_MOUSEMOVE + 2] = abs(ev->data2);
G_GetDeviceGameKeyDownArray(ev->device)[KEY_MOUSEMOVE + 3] = 0;
}
else
{
// Right
gamekeydown[ev->device][KEY_MOUSEMOVE + 2] = 0;
gamekeydown[ev->device][KEY_MOUSEMOVE + 3] = abs(ev->data2);
G_GetDeviceGameKeyDownArray(ev->device)[KEY_MOUSEMOVE + 2] = 0;
G_GetDeviceGameKeyDownArray(ev->device)[KEY_MOUSEMOVE + 3] = abs(ev->data2);
}
// Y axis
if (ev->data3 < 0)
{
// Up
gamekeydown[ev->device][KEY_MOUSEMOVE] = abs(ev->data3);
gamekeydown[ev->device][KEY_MOUSEMOVE + 1] = 0;
G_GetDeviceGameKeyDownArray(ev->device)[KEY_MOUSEMOVE] = abs(ev->data3);
G_GetDeviceGameKeyDownArray(ev->device)[KEY_MOUSEMOVE + 1] = 0;
}
else
{
// Down
gamekeydown[ev->device][KEY_MOUSEMOVE] = 0;
gamekeydown[ev->device][KEY_MOUSEMOVE + 1] = abs(ev->data3);
G_GetDeviceGameKeyDownArray(ev->device)[KEY_MOUSEMOVE] = 0;
G_GetDeviceGameKeyDownArray(ev->device)[KEY_MOUSEMOVE + 1] = abs(ev->data3);
}
break;
case ev_joystick: // buttons are virtual keys
case ev_gamepad_axis: // buttons are virtual keys
if (ev->data1 >= JOYAXISSETS)
{
#ifdef PARANOIA
@ -190,12 +434,12 @@ void G_MapEventsToControls(event_t *ev)
if (ev->data2 != INT32_MAX)
{
gamekeydown[ev->device][KEY_AXIS1 + (JOYANALOGS * 4) + (i * 2)] = max(0, ev->data2);
G_GetDeviceGameKeyDownArray(ev->device)[KEY_AXIS1 + (JOYANALOGS * 4) + (i * 2)] = max(0, ev->data2);
}
if (ev->data3 != INT32_MAX)
{
gamekeydown[ev->device][KEY_AXIS1 + (JOYANALOGS * 4) + (i * 2) + 1] = max(0, ev->data3);
G_GetDeviceGameKeyDownArray(ev->device)[KEY_AXIS1 + (JOYANALOGS * 4) + (i * 2) + 1] = max(0, ev->data3);
}
}
else
@ -206,14 +450,14 @@ void G_MapEventsToControls(event_t *ev)
if (ev->data2 < 0)
{
// Left
gamekeydown[ev->device][KEY_AXIS1 + (i * 4)] = abs(ev->data2);
gamekeydown[ev->device][KEY_AXIS1 + (i * 4) + 1] = 0;
G_GetDeviceGameKeyDownArray(ev->device)[KEY_AXIS1 + (i * 4)] = abs(ev->data2);
G_GetDeviceGameKeyDownArray(ev->device)[KEY_AXIS1 + (i * 4) + 1] = 0;
}
else
{
// Right
gamekeydown[ev->device][KEY_AXIS1 + (i * 4)] = 0;
gamekeydown[ev->device][KEY_AXIS1 + (i * 4) + 1] = abs(ev->data2);
G_GetDeviceGameKeyDownArray(ev->device)[KEY_AXIS1 + (i * 4)] = 0;
G_GetDeviceGameKeyDownArray(ev->device)[KEY_AXIS1 + (i * 4) + 1] = abs(ev->data2);
}
}
@ -222,14 +466,14 @@ void G_MapEventsToControls(event_t *ev)
if (ev->data3 < 0)
{
// Up
gamekeydown[ev->device][KEY_AXIS1 + (i * 4) + 2] = abs(ev->data3);
gamekeydown[ev->device][KEY_AXIS1 + (i * 4) + 3] = 0;
G_GetDeviceGameKeyDownArray(ev->device)[KEY_AXIS1 + (i * 4) + 2] = abs(ev->data3);
G_GetDeviceGameKeyDownArray(ev->device)[KEY_AXIS1 + (i * 4) + 3] = 0;
}
else
{
// Down
gamekeydown[ev->device][KEY_AXIS1 + (i * 4) + 2] = 0;
gamekeydown[ev->device][KEY_AXIS1 + (i * 4) + 3] = abs(ev->data3);
G_GetDeviceGameKeyDownArray(ev->device)[KEY_AXIS1 + (i * 4) + 2] = 0;
G_GetDeviceGameKeyDownArray(ev->device)[KEY_AXIS1 + (i * 4) + 3] = abs(ev->data3);
}
}
}

View file

@ -107,8 +107,8 @@ extern consvar_t cv_controlperkey;
// current state of the keys: JOYAXISRANGE or 0 when boolean.
// Or anything inbetween for analog values
#define MAXDEVICES (MAXGAMEPADS + 1) // Gamepads + keyboard & mouse
#define KEYBOARD_MOUSE_DEVICE 0
extern INT32 gamekeydown[MAXDEVICES][NUMINPUTS];
extern boolean deviceResponding[MAXDEVICES];
// several key codes (or virtual key) per game control
extern INT32 gamecontrol[MAXSPLITSCREENPLAYERS][num_gamecontrols][MAXINPUTMAPPING];
@ -135,7 +135,31 @@ extern const INT32 gcl_full[num_gcl_full];
// peace to my little coder fingers!
// check a gamecontrol being active or not
INT32 G_GetDevicePlayer(INT32 deviceID);
/*
*/
/// Register a device index (from ev_gamepad_device_added) as an Available Gamepad
void G_RegisterAvailableGamepad(INT32 device_id);
/// Unregister a device index (from ev_gamepad_device_removed) as an Available Gamepad
void G_UnregisterAvailableGamepad(INT32 device_id);
/// Get the number of Available Gamepads registered.
INT32 G_GetNumAvailableGamepads(void);
/// Get the device ID for a given Available Gamepad Index, or -1. 0 <= available_index < G_GetNumAvailableGamepads()
INT32 G_GetAvailableGamepadDevice(INT32 available_index);
INT32 G_GetPlayerForDevice(INT32 deviceID);
/// Get gamepad device for given player, or -1.
INT32 G_GetDeviceForPlayer(INT32 player);
/// Set the given player index's assigned device. If the device is in use by another player, that player is unassigned.
void G_SetDeviceForPlayer(INT32 player, INT32 device);
/// Get the gamekeydown array (NUMINPUTS values) for the given device, or NULL if the device id is invalid.
INT32* G_GetDeviceGameKeyDownArray(INT32 device);
boolean G_IsDeviceResponding(INT32 device);
void G_SetDeviceResponding(INT32 device, boolean responding);
void G_ResetAllDeviceResponding(void);
// remaps the input event to a game control.
void G_MapEventsToControls(event_t *ev);

View file

@ -58,6 +58,9 @@ struct JoyType_t
extern JoyType_t Joystick[MAXSPLITSCREENPLAYERS];
void I_SetGamepadPlayerIndex(INT32 device_id, INT32 index);
void I_SetGamepadIndicatorColor(INT32 device_id, UINT8 red, UINT8 green, UINT8 blue);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -205,9 +205,8 @@ void I_JoyScale4(void);
// Called by D_SRB2Main.
/** \brief to startup a joystick
*/
void I_InitJoystick(UINT8 index);
/// Startup input subsystems.
void I_StartupInput(void);
/** \brief to startup the first joystick
*/

View file

@ -60,13 +60,6 @@
// And just some randomness for the exits.
#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
#include <stdio.h> // for snprintf
int snprintf(char *str, size_t n, const char *fmt, ...);
@ -3385,11 +3378,18 @@ void M_DrawProfileControls(void)
// Get userbound controls...
for (k = 0; k < MAXINPUTMAPPING; k++)
{
int device;
keys[k] = optionsmenu.tempcontrols[gc][k];
if (keys[k] == KEY_NULL)
continue;
set++;
if (!G_KeyIsAvailable(keys[k], cv_usejoystick[0].value))
device = G_GetDeviceForPlayer(0);
if (device == -1)
{
device = 0;
}
if (!G_KeyIsAvailable(keys[k], device))
continue;
available++;
};

View file

@ -222,6 +222,22 @@ boolean M_Responder(event_t *ev)
return false;
}
if (gamestate == GS_MENU && ev->type == ev_gamepad_device_removed && G_GetPlayerForDevice(ev->device) != -1)
{
int i;
INT32 player = G_GetPlayerForDevice(ev->device);
// Unassign all controllers
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
G_SetDeviceForPlayer(i, -1);
}
// Return to the title because a controller was removed at the menu.
CONS_Alert(CONS_NOTICE, "Player %d's assigned gamepad was removed. Returning to the title screen.", player);
D_StartTitle();
}
if (ev->type == ev_keydown && ev->data1 < NUMKEYS)
{
// Record keyboard presses

View file

@ -123,7 +123,7 @@ static void SetDeviceOnPress(void)
{
if (deviceResponding[i])
{
CV_SetValue(&cv_usejoystick[0], i); // Force-set this joystick as the current joystick we're using for P1 (which is the only one controlling menus)
G_SetDeviceForPlayer(0, i); // Force-set this joystick as the current joystick we're using for P1 (which is the only one controlling menus)
CONS_Printf("SetDeviceOnPress: Device for %d set to %d\n", 0, i);
return;
}
@ -307,7 +307,7 @@ void M_MapProfileControl(event_t *ev)
UINT8 where = n; // By default, we'll save the bind where we're supposed to map.
INT32 i;
//SetDeviceOnPress(); // Update cv_usejoystick
//SetDeviceOnPress(); // Update player gamepad assignments
// Only consider keydown and joystick events to make sure we ignore ev_mouse and other events
// See also G_MapEventsToControls
@ -325,11 +325,11 @@ void M_MapProfileControl(event_t *ev)
}
#endif
break;
case ev_joystick:
case ev_gamepad_axis:
if (ev->data1 >= JOYAXES)
{
#ifdef PARANOIA
CONS_Debug(DBG_GAMELOGIC, "Bad joystick axis event %d\n", ev->data1);
CONS_Debug(DBG_GAMELOGIC, "Bad gamepad axis event %d\n", ev->data1);
#endif
return;
}

View file

@ -380,10 +380,10 @@ void M_CharacterSelectInit(void)
{
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
// Un-set devices for other players.
if (i != 0 || optionsmenu.profile)
// Un-set devices for all players if not editing profile
if (!optionsmenu.profile)
{
CV_SetValue(&cv_usejoystick[i], -1);
G_SetDeviceForPlayer(i, -1);
CONS_Printf("M_CharacterSelectInit: Device for %d set to %d\n", i, -1);
}
}
@ -526,7 +526,12 @@ static boolean M_DeviceAvailable(INT32 deviceID, UINT8 numPlayers)
for (i = 0; i < numPlayers; i++)
{
if (cv_usejoystick[i].value == deviceID)
int player_device = G_GetDeviceForPlayer(i);
if (player_device == -1)
{
continue;
}
if (player_device == deviceID)
{
// This one's already being used.
return false;
@ -540,6 +545,7 @@ static boolean M_DeviceAvailable(INT32 deviceID, UINT8 numPlayers)
static boolean M_HandlePressStart(setup_player_t *p, UINT8 num)
{
INT32 i, j;
INT32 num_gamepads_available;
if (optionsmenu.profile)
return false; // Don't allow for the possibility of SOMEHOW another player joining in.
@ -568,24 +574,32 @@ static boolean M_HandlePressStart(setup_player_t *p, UINT8 num)
}
// Now detect new devices trying to join.
for (i = 0; i < MAXDEVICES; i++)
num_gamepads_available = G_GetNumAvailableGamepads();
for (i = 0; i < num_gamepads_available + 1; i++)
{
if (deviceResponding[i] != true)
INT32 device = 0;
if (i > 0)
{
device = G_GetAvailableGamepadDevice(i - 1);
}
if (G_IsDeviceResponding(device) != true)
{
// No buttons are being pushed.
continue;
}
if (M_DeviceAvailable(i, setup_numplayers) == true)
if (M_DeviceAvailable(device, setup_numplayers) == true)
{
// Available!! Let's use this one!!
// if P1 is setting up using keyboard (device 0), save their last used device.
// this is to allow them to retain controller usage when they play alone.
// Because let's face it, when you test mods, you're often lazy to grab your controller for menuing :)
if (!i && !num)
if (i == 0 && num == 0)
{
setup_player[num].ponedevice = cv_usejoystick[num].value;
setup_player[num].ponedevice = G_GetDeviceForPlayer(num);
}
else if (num)
{
@ -593,14 +607,13 @@ static boolean M_HandlePressStart(setup_player_t *p, UINT8 num)
memcpy(&gamecontrol[num], gamecontroldefault, sizeof(gamecontroldefault));
}
G_SetDeviceForPlayer(num, device);
CONS_Printf("M_HandlePressStart: Device for %d set to %d\n", num, device);
CV_SetValue(&cv_usejoystick[num], i);
CONS_Printf("M_HandlePressStart: Device for %d set to %d\n", num, i);
for (j = num+1; j < MAXSPLITSCREENPLAYERS; j++)
for (j = num + 1; j < MAXSPLITSCREENPLAYERS; j++)
{
// Un-set devices for other players.
CV_SetValue(&cv_usejoystick[j], -1);
G_SetDeviceForPlayer(j, -1);
CONS_Printf("M_HandlePressStart: Device for %d set to %d\n", j, -1);
}
@ -616,7 +629,7 @@ static boolean M_HandlePressStart(setup_player_t *p, UINT8 num)
menucmd[j].buttonsHeld |= MBT_X;
}
memset(deviceResponding, false, sizeof(deviceResponding));
G_ResetAllDeviceResponding();
return true;
}
}
@ -668,11 +681,8 @@ static boolean M_HandleCSelectProfile(setup_player_t *p, UINT8 num)
menucmd[i].buttonsHeld |= MBT_X;
}
if (num > 0)
{
CV_StealthSetValue(&cv_usejoystick[num], -1);
CONS_Printf("M_HandleCSelectProfile: Device for %d set to %d\n", num, -1);
}
G_SetDeviceForPlayer(num, -1);
CONS_Printf("M_HandleCSelectProfile: Device for %d set to %d\n", num, -1);
return true;
}
@ -1482,18 +1492,12 @@ void M_CharacterSelectTick(void)
CV_StealthSetValue(&cv_splitplayers, setup_numplayers);
// P1 is alone, set their old device just in case.
if (setup_numplayers < 2 && setup_player[0].ponedevice)
{
CV_StealthSetValue(&cv_usejoystick[0], setup_player[0].ponedevice);
}
#if defined (TESTERS)
M_MPOptSelectInit(0);
#else
M_SetupNextMenu(&PLAY_MainDef, false);
#endif
}
}
else // In a game

View file

@ -194,48 +194,6 @@ static char returnWadPath[256];
#include "../byteptr.h"
#endif
void I_StoreExJoystick(SDL_GameController *dev)
{
// ExJoystick is a massive hack to avoid needing to completely
// rewrite pretty much all of the controller support from scratch...
// Used in favor of most instances of SDL_GameControllerClose.
// If a joystick would've been discarded, then save it in an array,
// because we want it have it for the joystick input screen.
int index = 0;
if (dev == NULL)
{
// No joystick?
return;
}
index = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(dev));
if (index >= MAXGAMEPADS || index < 0)
{
// Not enough space to save this joystick, completely discard.
SDL_GameControllerClose(dev);
return;
}
if (ExJoystick[index] == dev)
{
// No need to do anything else.
return;
}
if (ExJoystick[index] != NULL)
{
// Discard joystick in the old slot.
SDL_GameControllerClose(ExJoystick[index]);
}
// Keep for safe-keeping.
ExJoystick[index] = dev;
}
/** \brief The JoyReset function
\param JoySet Joystick info to reset
@ -244,10 +202,6 @@ void I_StoreExJoystick(SDL_GameController *dev)
*/
static void JoyReset(SDLJoyInfo_t *JoySet)
{
if (JoySet->dev)
{
I_StoreExJoystick(JoySet->dev);
}
JoySet->dev = NULL;
JoySet->oldjoy = -1;
JoySet->axises = JoySet->buttons = JoySet->hats = JoySet->balls = 0;
@ -261,7 +215,6 @@ static INT32 joystick_started[MAXSPLITSCREENPLAYERS] = {0,0,0,0};
/** \brief SDL info about joystick 1
*/
SDLJoyInfo_t JoyInfo[MAXSPLITSCREENPLAYERS];
SDL_GameController *ExJoystick[MAXGAMEPADS];
SDL_bool consolevent = SDL_FALSE;
SDL_bool framebuffer = SDL_FALSE;
@ -983,316 +936,78 @@ void I_JoyScale4(void)
JoyInfo[3].scale = Joystick[3].bGamepadStyle?1:cv_joyscale[1].value;
}
// Cheat to get the device index for a game controller handle
INT32 I_GetJoystickDeviceIndex(SDL_GameController *dev)
void I_SetGamepadPlayerIndex(INT32 device_id, INT32 player)
{
SDL_Joystick *joystick = NULL;
#if !(SDL_VERSION_ATLEAST(2,0,12))
(void)device_id;
(void)player;
#else
I_Assert(device_id > 0); // Gamepad devices are always ID 1 or higher
I_Assert(player >= 0 && player < MAXSPLITSCREENPLAYERS);
joystick = SDL_GameControllerGetJoystick(dev);
if (joystick)
SDL_GameController *controller = SDL_GameControllerFromInstanceID(device_id - 1);
if (controller == NULL)
{
return SDL_JoystickInstanceID(joystick);
return;
}
return -1;
SDL_GameControllerSetPlayerIndex(controller, player);
#endif
}
void I_UpdateJoystickDeviceIndex(UINT8 player)
void I_SetGamepadIndicatorColor(INT32 device_id, UINT8 red, UINT8 green, UINT8 blue)
{
///////////////////////////////////////////////
// update this joystick's device index (wow) //
///////////////////////////////////////////////
#if !(SDL_VERSION_ATLEAST(2,0,14))
(void)device_id;
(void)player;
#else
I_Assert(device_id > 0); // Gamepad devices are always ID 1 or higher
if (JoyInfo[player].dev)
SDL_GameController *controller = SDL_GameControllerFromInstanceID(device_id - 1);
if (controller == NULL)
{
cv_usejoystick[player].value = I_GetJoystickDeviceIndex(JoyInfo[player].dev) + 1;
CONS_Printf("I_UpdateJoystickDeviceIndex: Device for %d set to %d\n", player, cv_usejoystick[player].value);
}
else
{
UINT8 joystickID, compareJoystick;
for (joystickID = 0; joystickID < MAXSPLITSCREENPLAYERS; joystickID++)
{
// is this cv_usejoystick used?
const INT32 value = atoi(cv_usejoystick[joystickID].string);
for (compareJoystick = 0; compareJoystick < MAXSPLITSCREENPLAYERS; compareJoystick++)
{
if (compareJoystick == player)
continue;
if (value == JoyInfo[compareJoystick].oldjoy || value == cv_usejoystick[compareJoystick].value)
break;
}
if (compareJoystick == MAXSPLITSCREENPLAYERS)
{
// We DID make it through the whole loop, so we can use this one!
cv_usejoystick[player].value = value;
CONS_Printf("I_UpdateJoystickDeviceIndex: Device for %d set to %d\n", player, cv_usejoystick[player].value);
break;
}
}
if (joystickID == MAXSPLITSCREENPLAYERS)
{
// We DID NOT make it through the whole loop, so we can't assign this joystick to anything.
// When you try your best, but you don't succeed...
cv_usejoystick[player].value = 0;
CONS_Printf("I_UpdateJoystickDeviceIndex: Device for %d set to %d\n", player, 0);
}
}
}
// Misleading function: updates device indices for all players BUT the one specified.
// Necessary for SDL_JOYDEVICEADDED events
void I_UpdateJoystickDeviceIndices(UINT8 excludePlayer)
{
UINT8 player;
for (player = 0; player < MAXSPLITSCREENPLAYERS; player++)
{
if (player == excludePlayer)
continue;
I_UpdateJoystickDeviceIndex(player);
}
}
/** \brief Shuts down joystick
\return void
*/
void I_ShutdownJoystick(UINT8 index)
{
INT32 i;
event_t event;
event.device = I_GetJoystickDeviceIndex(JoyInfo[index].dev);
event.type = ev_keyup;
event.data2 = 0;
event.data3 = 0;
// emulate the up of all joystick buttons
for (i = 0; i < JOYBUTTONS; i++)
{
event.data1 = KEY_JOY1+i;
D_PostEvent(&event);
return;
}
// reset joystick position
event.type = ev_joystick;
for (i = 0; i < JOYAXES; i++)
{
event.data1 = i;
D_PostEvent(&event);
}
joystick_started[index] = 0;
JoyReset(&JoyInfo[index]);
// don't shut down the subsystem here, because hotplugging
}
/** \brief Open joystick handle
\param fname name of joystick
\return axises
*/
static int joy_open(int playerIndex, int joyIndex)
{
SDL_GameController *newdev = NULL;
int num_joy = 0;
if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
{
CONS_Printf(M_GetText("Joystick subsystem not started\n"));
return -1;
}
if (SDL_WasInit(SDL_INIT_GAMECONTROLLER) == 0)
{
CONS_Printf(M_GetText("Game Controller subsystem not started\n"));
return -1;
}
if (joyIndex <= 0)
return -1;
num_joy = SDL_NumJoysticks();
if (num_joy == 0)
{
CONS_Printf("%s", M_GetText("Found no joysticks on this system\n"));
return -1;
}
newdev = SDL_GameControllerOpen(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[playerIndex].dev)
{
if (JoyInfo[playerIndex].dev == newdev // same device, nothing to do
|| (newdev == NULL && SDL_GameControllerGetAttached(JoyInfo[playerIndex].dev))) // we failed, but already have a working device
{
return SDL_CONTROLLER_AXIS_MAX;
}
// Else, we're changing devices, so send neutral joy events
CONS_Debug(DBG_GAMELOGIC, "Joystick%d device is changing; resetting events...\n", playerIndex+1);
I_ShutdownJoystick(playerIndex);
}
JoyInfo[playerIndex].dev = newdev;
if (JoyInfo[playerIndex].dev == NULL)
{
CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick%d: Couldn't open device - %s\n"), playerIndex+1, SDL_GetError());
return -1;
}
else
{
CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick%d: %s\n"), playerIndex+1, SDL_GameControllerName(JoyInfo[playerIndex].dev));
JoyInfo[playerIndex].axises = SDL_CONTROLLER_AXIS_MAX;
JoyInfo[playerIndex].buttons = SDL_CONTROLLER_BUTTON_MAX;
JoyInfo[playerIndex].hats = 1;
JoyInfo[playerIndex].balls = 0;
//JoyInfo[playerIndex].bGamepadStyle = !stricmp(SDL_JoystickName(JoyInfo[playerIndex].dev), "pad");
return JoyInfo[playerIndex].axises;
}
SDL_GameControllerSetLED(controller, red, green, blue);
#endif
}
//
// I_InitJoystick
// I_StartupInput
//
void I_InitJoystick(UINT8 index)
void I_StartupInput(void)
{
SDL_GameController *newcontroller = NULL;
UINT8 i;
//I_ShutdownJoystick();
//SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE);
if (M_CheckParm("-nojoy"))
return;
if (SDL_WasInit(SDL_INIT_GAMECONTROLLER))
{
return;
}
if (M_CheckParm("-noxinput"))
SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE);
if (M_CheckParm("-nohidapi"))
SDL_SetHintWithPriority("SDL_JOYSTICK_HIDAPI", "0", SDL_HINT_OVERRIDE);
if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
{
CONS_Printf("I_InitJoystick()...\n");
CONS_Printf("I_StartupInput()...\n");
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1)
{
CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError());
return;
}
if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == -1)
{
CONS_Printf(M_GetText("Couldn't initialize game controllers: %s\n"), SDL_GetError());
return;
}
if (SDL_WasInit(SDL_INIT_GAMECONTROLLER) == 0)
{
if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == -1)
{
CONS_Printf(M_GetText("Couldn't initialize gamepads: %s\n"), SDL_GetError());
return;
}
}
if (cv_usejoystick[index].value)
newcontroller = SDL_GameControllerOpen(cv_usejoystick[index].value-1);
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
if (i == index)
continue;
if (JoyInfo[i].dev == newcontroller)
break;
}
if (newcontroller && i < MAXSPLITSCREENPLAYERS) // don't override an active device
{
cv_usejoystick[index].value = I_GetJoystickDeviceIndex(JoyInfo[index].dev) + 1;
CONS_Printf("I_InitJoystick: Device for %d set to %d\n", index, cv_usejoystick[index].value);
}
else if (newcontroller && joy_open(index, cv_usejoystick[index].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[index].oldjoy = I_GetJoystickDeviceIndex(JoyInfo[index].dev) + 1;
joystick_started[index] = 1;
}
else
{
if (JoyInfo[index].oldjoy)
I_ShutdownJoystick(index);
cv_usejoystick[index].value = 0;
CONS_Printf("I_InitJoystick: Device for %d set to %d\n", index, cv_usejoystick[index].value);
joystick_started[index] = 0;
}
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
if (JoyInfo[i].dev == newcontroller)
break;
}
if (i == MAXSPLITSCREENPLAYERS)
{
// Joystick didn't end up being used
I_StoreExJoystick(newcontroller);
}
}
void I_InitJoystick1(void)
{
I_InitJoystick(0);
}
void I_InitJoystick2(void)
{
I_InitJoystick(1);
}
void I_InitJoystick3(void)
{
I_InitJoystick(2);
}
void I_InitJoystick4(void)
{
I_InitJoystick(3);
// Upon initialization, the gamecontroller subsystem will automatically dispatch controller device added events
// for controllers connected before initialization.
}
static void I_ShutdownInput(void)
{
UINT8 i;
// 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.
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
I_ShutdownJoystick(i);
// The game code is now responsible for resetting its internal state based on ev_gamepad_device_removed events.
// In practice, Input should never be shutdown and restarted during runtime.
if (SDL_WasInit(SDL_INIT_GAMECONTROLLER) == SDL_INIT_GAMECONTROLLER)
{
@ -1322,14 +1037,28 @@ static char joyname[255]; // joystick name is straight from the driver
const char *I_GetJoyName(INT32 joyindex)
{
const char *tempname = NULL;
SDL_Joystick* joystick;
joyname[0] = 0;
joyindex--; //SDL's Joystick System starts at 0, not 1
if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK)
if (!SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK)
{
tempname = SDL_JoystickNameForIndex(joyindex);
if (tempname)
strncpy(joyname, tempname, 255);
return joyname;
}
// joyindex corresponds to the open joystick *instance* ID, not the joystick number
joystick = SDL_JoystickFromInstanceID(joyindex);
if (joystick == NULL)
{
return joyname;
}
tempname = SDL_JoystickNameForIndex(joyindex);
if (tempname)
{
strncpy(joyname, tempname, 255);
}
return joyname;
}

View file

@ -542,7 +542,7 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt)
SDLforceUngrabMouse();
}
memset(gamekeydown, 0, sizeof(gamekeydown)); // TODO this is a scary memset
memset(deviceResponding, false, sizeof (deviceResponding));
G_ResetAllDeviceResponding();
if (MOUSE_MENU)
{
@ -701,7 +701,7 @@ static void Impl_HandleControllerAxisEvent(SDL_ControllerAxisEvent evt)
event_t event;
INT32 value;
event.type = ev_joystick;
event.type = ev_gamepad_axis;
event.device = 1 + evt.which;
if (event.device == INT32_MAX)
@ -777,6 +777,40 @@ static void Impl_HandleControllerButtonEvent(SDL_ControllerButtonEvent evt, Uint
}
}
static void Impl_HandleControllerDeviceAddedEvent(SDL_ControllerDeviceEvent event)
{
// The game is always interested in controller events, even if they aren't internally assigned to a player.
// Thus, we *always* open SDL controllers as they become available, to begin receiving their events.
SDL_GameController* controller = SDL_GameControllerOpen(event.which);
if (controller == NULL)
{
return;
}
SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller);
SDL_JoystickID joystick_instance_id = SDL_JoystickInstanceID(joystick);
event_t engine_event {};
engine_event.type = ev_gamepad_device_added;
engine_event.device = 1 + joystick_instance_id;
D_PostEvent(&engine_event);
}
static void Impl_HandleControllerDeviceRemovedEvent(SDL_ControllerDeviceEvent event)
{
// SDL only posts Device Removed events for controllers that have actually been opened.
// Thus, we don't need to filter out controllers that may not have opened successfully prior to this event.
event_t engine_event {};
engine_event.type = ev_gamepad_device_removed;
engine_event.device = 1 + event.which;
D_PostEvent(&engine_event);
}
static ImGuiKey ImGui_ImplSDL2_KeycodeToImGuiKey(int keycode)
{
switch (keycode)
@ -983,8 +1017,6 @@ void I_GetEvent(void)
// otherwise we'll end up catching the warp back to center.
//int mouseMotionOnce = 0;
UINT8 i;
if (!graphics_started)
{
return;
@ -1031,147 +1063,14 @@ void I_GetEvent(void)
Impl_HandleControllerButtonEvent(evt.cbutton, evt.type);
break;
////////////////////////////////////////////////////////////
case SDL_CONTROLLERDEVICEADDED:
{
// OH BOY are you in for a good time! #abominationstation
SDL_GameController *newcontroller = SDL_GameControllerOpen(evt.cdevice.which);
CONS_Debug(DBG_GAMELOGIC, "Controller device index %d added\n", evt.cdevice.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!
////////////////////////////////////////////////////////////
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
if (newcontroller && (!JoyInfo[i].dev || !SDL_GameControllerGetAttached(JoyInfo[i].dev)))
{
UINT8 j;
for (j = 0; j < MAXSPLITSCREENPLAYERS; j++)
{
if (i == j)
continue;
if (JoyInfo[j].dev == newcontroller)
break;
}
if (j == MAXSPLITSCREENPLAYERS)
{
// ensures we aren't overriding a currently active device
cv_usejoystick[i].value = evt.cdevice.which + 1;
I_UpdateJoystickDeviceIndices(0);
}
}
}
////////////////////////////////////////////////////////////
// Was cv_usejoystick disabled in settings?
////////////////////////////////////////////////////////////
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
if (!strcmp(cv_usejoystick[i].string, "0") || !cv_usejoystick[i].value)
cv_usejoystick[i].value = 0;
else if (atoi(cv_usejoystick[i].string) <= I_NumJoys() // don't mess if we intentionally set higher than NumJoys
&& cv_usejoystick[i].value) // update the cvar ONLY if a device exists
CV_SetValue(&cv_usejoystick[i], cv_usejoystick[i].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.
////////////////////////////////////////////////////////////
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
I_InitJoystick(i);
////////////////////////////////////////////////////////////
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
CONS_Debug(DBG_GAMELOGIC, "Joystick%d device index: %d\n", i+1, JoyInfo[i].oldjoy);
#if 0
// update the menu
if (currentMenu == &OP_JoystickSetDef)
M_SetupJoystickMenu(0);
#endif
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
if (JoyInfo[i].dev == newcontroller)
break;
}
if (i == MAXSPLITSCREENPLAYERS)
I_StoreExJoystick(newcontroller);
}
Impl_HandleControllerDeviceAddedEvent(evt.cdevice);
break;
////////////////////////////////////////////////////////////
case SDL_CONTROLLERDEVICEREMOVED:
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
if (JoyInfo[i].dev && !SDL_GameControllerGetAttached(JoyInfo[i].dev))
{
CONS_Debug(DBG_GAMELOGIC, "Joystick%d removed, device index: %d\n", i+1, JoyInfo[i].oldjoy);
I_ShutdownJoystick(i);
}
}
////////////////////////////////////////////////////////////
// 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!
////////////////////////////////////////////////////////////
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
I_UpdateJoystickDeviceIndex(i);
}
////////////////////////////////////////////////////////////
// Was cv_usejoystick disabled in settings?
////////////////////////////////////////////////////////////
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
if (!strcmp(cv_usejoystick[i].string, "0"))
{
cv_usejoystick[i].value = 0;
}
else if (atoi(cv_usejoystick[i].string) <= I_NumJoys() // don't mess if we intentionally set higher than NumJoys
&& cv_usejoystick[i].value) // update the cvar ONLY if a device exists
{
CV_SetValue(&cv_usejoystick[i], cv_usejoystick[i].value);
}
}
////////////////////////////////////////////////////////////
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
CONS_Debug(DBG_GAMELOGIC, "Joystick%d device index: %d\n", i+1, JoyInfo[i].oldjoy);
#if 0
// update the menu
if (currentMenu == &OP_JoystickSetDef)
M_SetupJoystickMenu(0);
#endif
Impl_HandleControllerDeviceRemovedEvent(evt.cdevice);
break;
case SDL_QUIT:
LUA_HookBool(true, HOOK(GameQuit));
I_Quit();
@ -1195,10 +1094,7 @@ void I_GetEvent(void)
// In order to make wheels act like buttons, we have to set their state to Up.
// This is because wheel messages don't have an up/down state.
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
gamekeydown[i][KEY_MOUSEWHEELDOWN] = gamekeydown[i][KEY_MOUSEWHEELUP] = 0;
}
G_GetDeviceGameKeyDownArray(0)[KEY_MOUSEWHEELDOWN] = G_GetDeviceGameKeyDownArray(0)[KEY_MOUSEWHEELUP] = 0;
}
static void half_warp_mouse(uint16_t x, uint16_t y) {

View file

@ -36,9 +36,6 @@ extern "C" {
#define SDL2STUB() CONS_Printf("SDL2: stubbed: %s:%d\n", __func__, __LINE__)
#endif
// So m_menu knows whether to store cv_usejoystick value or string
#define JOYSTICK_HOTPLUG
/** \brief The JoyInfo_s struct
info about joystick
@ -65,9 +62,6 @@ typedef struct SDLJoyInfo_s
/** \brief SDL info about controllers
*/
extern SDLJoyInfo_t JoyInfo[MAXSPLITSCREENPLAYERS];
extern SDL_GameController *ExJoystick[MAXGAMEPADS];
void I_StoreExJoystick(SDL_GameController *dev);
/** \brief joystick axis deadzone
*/
@ -76,18 +70,6 @@ void I_StoreExJoystick(SDL_GameController *dev);
void I_GetConsoleEvents(void);
// So we can call this from i_video event loop
void I_ShutdownJoystick(UINT8 index);
// Cheat to get the device index for a game controller handle
INT32 I_GetJoystickDeviceIndex(SDL_GameController *dev);
// Quick thing to make SDL_JOYDEVICEADDED events less of an abomination
void I_UpdateJoystickDeviceIndex(UINT8 player);
void I_UpdateJoystickDeviceIndices(UINT8 excludePlayer);
void I_GetConsoleEvents(void);
void SDLforceUngrabMouse(void);
// Needed for some WIN32 functions