Merge branch 'dynamic-controllers' into 'master'

Handle gamepads from interface dynamically

Closes #397

See merge request KartKrew/Kart!1018
This commit is contained in:
Oni 2023-03-13 07:56:34 +00:00
commit 9f4c080964
18 changed files with 714 additions and 726 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();

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

@ -172,6 +172,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
@ -184,11 +232,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
@ -989,7 +1039,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)
@ -1521,6 +1571,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),
@ -1040,7 +1030,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]);
@ -6231,6 +6220,7 @@ static void FollowerAny_OnChange(UINT8 pnum)
return; // don't send anything there.
SendNameAndColor(pnum);
G_SetPlayerGamepadIndicatorToPlayerColor(pnum);
}
// sends the follower change for players
@ -6382,6 +6372,8 @@ static void Color_OnChange(void)
}
}
lastgoodcolor[0] = cv_playercolor[0].value;
G_SetPlayerGamepadIndicatorToPlayerColor(0);
}
/** Sends a color change for the secondary splitscreen player, unless that
@ -6410,6 +6402,8 @@ static void Color2_OnChange(void)
}
}
lastgoodcolor[1] = cv_playercolor[1].value;
G_SetPlayerGamepadIndicatorToPlayerColor(1);
}
static void Color3_OnChange(void)
@ -6433,6 +6427,8 @@ static void Color3_OnChange(void)
}
}
lastgoodcolor[2] = cv_playercolor[2].value;
G_SetPlayerGamepadIndicatorToPlayerColor(2);
}
static void Color4_OnChange(void)
@ -6456,6 +6452,8 @@ static void Color4_OnChange(void)
}
}
lastgoodcolor[3] = cv_playercolor[3].value;
G_SetPlayerGamepadIndicatorToPlayerColor(3);
}
/** Displays the result of the chat being muted or unmuted.

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

@ -866,32 +866,49 @@ INT16 G_SoftwareClipAimingPitch(INT32 *aiming)
return (INT16)((*aiming)>>16);
}
// Default controls for keyboard. These are hardcoded and cannot be changed.
static INT32 keyboardMenuDefaults[][2] = {
{gc_a, KEY_ENTER},
{gc_c, KEY_BACKSPACE},
{gc_x, KEY_ESCAPE},
{gc_left, KEY_LEFTARROW},
{gc_right, KEY_RIGHTARROW},
{gc_up, KEY_UPARROW},
{gc_down, KEY_DOWNARROW},
static INT32 G_GetValueFromControlTable(INT32 deviceID, INT32 deadzone, INT32 *controltable)
{
INT32 i;
// special control
{gc_start, KEY_ESCAPE},
// 8 total controls*
};
if (deviceID <= UNASSIGNED_DEVICE)
{
// An invalid device can't have any binds!
return 0;
}
#define KEYBOARDDEFAULTSSPLIT 7
for (i = 0; i < MAXINPUTMAPPING; i++)
{
INT32 key = controltable[i];
INT32 value = 0;
// Invalid key number.
if (G_KeyIsAvailable(key, deviceID) == false)
{
continue;
}
value = G_GetDeviceGameKeyDownArray(deviceID)[key];
if (value >= deadzone)
{
return value;
}
}
// Not pressed.
return 0;
}
INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, UINT8 menuPlayers)
{
INT32 deviceID;
INT32 i, j;
INT32 deadzone = 0;
boolean trydefaults = true;
boolean tryingotherID = false;
INT32 *controltable = &(gamecontrol[p][gc][0]);
const INT32 deadzone = (JOYAXISRANGE * cv_deadzone[p].value) / FRACUNIT;
const INT32 keyboard_player = G_GetPlayerForDevice(KEYBOARD_MOUSE_DEVICE);
const boolean in_menu = (menuPlayers > 0);
const boolean main_player = (p == 0);
INT32 deviceID = UNASSIGNED_DEVICE;
INT32 value = -1;
INT32 avail_gamepad_id = 0;
INT32 i;
if (p >= MAXSPLITSCREENPLAYERS)
{
@ -901,118 +918,96 @@ INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, UINT8 menuPlayers)
return 0;
}
deadzone = (JOYAXISRANGE * cv_deadzone[p].value) / FRACUNIT;
deviceID = G_GetDeviceForPlayer(p);
deviceID = cv_usejoystick[p].value;
retrygetcontrol:
for (i = 0; i < MAXINPUTMAPPING; i++)
if ((in_menu == true && G_KeyBindIsNecessary(gc) == true) // In menu: check for all unoverrideable menu default controls.
|| (in_menu == false && gc == gc_start)) // In gameplay: check for the unoverrideable start button to be able to bring up the menu.
{
INT32 key = controltable[i];
INT32 menukey = KEY_NULL;
INT32 value = 0;
boolean processinput = true;
// for menus, keyboards have defaults!
if (deviceID == 0)
value = G_GetValueFromControlTable(KEYBOARD_MOUSE_DEVICE, JOYAXISRANGE/4, &(menucontrolreserved[gc][0]));
if (value > 0) // Check for press instead of bound.
{
// In menus, check indexes 0 through 5 (everything besides gc_start)
// Outside of menus, only consider the hardcoded input for gc_start at index 6
INT32 maxj = menuactive ? KEYBOARDDEFAULTSSPLIT : KEYBOARDDEFAULTSSPLIT+1;
j = (!menuactive) ? KEYBOARDDEFAULTSSPLIT : 0;
for (; j < maxj; j++) // check keyboardMenuDefaults
// This is only intended for P1.
if (main_player == true)
{
// the gc we're looking for
if (gc == keyboardMenuDefaults[j][0])
{
menukey = keyboardMenuDefaults[j][1];
break;
return value;
}
// The key is mapped to *something else*...?
// Then don't process that as it would conflict with our hardcoded inputs.
else if (key == keyboardMenuDefaults[j][1])
else
{
processinput = false;
break;
return 0;
}
}
}
// Invalid key number.
if (!G_KeyIsAvailable(key, deviceID) && !G_KeyIsAvailable(menukey, deviceID))
// Player 1 is always allowed to use the keyboard in 1P, even if they got disconnected.
if (main_player == true && keyboard_player == -1 && deviceID == UNASSIGNED_DEVICE)
{
deviceID = KEYBOARD_MOUSE_DEVICE;
}
// First, try our actual binds.
value = G_GetValueFromControlTable(deviceID, deadzone, &(gamecontrol[p][gc][0]));
if (value > 0)
{
return value;
}
// If you're on gamepad in 1P, and you didn't have a gamepad bind for this, then try your keyboard binds.
if (main_player == true && keyboard_player == -1 && deviceID > KEYBOARD_MOUSE_DEVICE)
{
value = G_GetValueFromControlTable(KEYBOARD_MOUSE_DEVICE, deadzone, &(gamecontrol[p][gc][0]));
if (value > 0)
{
return value;
}
}
if (in_menu == true)
{
if (main_player == true)
{
// We are P1 controlling menus. We should be able to
// control the menu with any unused gamepads, so
// that gamepads are able to navigate to the player
// setup menu in the first place.
for (avail_gamepad_id = 0; avail_gamepad_id < G_GetNumAvailableGamepads(); avail_gamepad_id++)
{
INT32 tryDevice = G_GetAvailableGamepadDevice(avail_gamepad_id);
if (tryDevice <= KEYBOARD_MOUSE_DEVICE)
{
continue;
}
if (processinput)
for (i = 0; i < menuPlayers; i++)
{
// It's possible to access this control right now, so let's disable the default control backup for later.
trydefaults = false;
if (tryDevice == G_GetDeviceForPlayer(i))
{
// Don't do this for already taken devices.
break;
}
}
value = gamekeydown[deviceID][key];
if (menukey && gamekeydown[deviceID][menukey])
value = gamekeydown[deviceID][menukey];
if (value >= deadzone)
if (i == menuPlayers)
{
// This gamepad isn't being used, so we can
// use it for P1 menu navigation.
value = G_GetValueFromControlTable(tryDevice, deadzone, &(gamecontrol[p][gc][0]));
if (value > 0)
{
return value;
}
}
}
// 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;
goto retrygetcontrol;
}
if (menuPlayers == 0)
// Still nothing bound after everything. Try default gamepad controls.
value = G_GetValueFromControlTable(deviceID, deadzone, &(gamecontroldefault[gc][0]));
if (value > 0)
{
return 0;
}
// We don't want menus to become unnavigable if people unbind
// all of their controls, so we do several things in this scenario.
// First: try other controllers.
if (!tryingotherID)
{
deviceID = MAXDEVICES;
tryingotherID = true;
}
loweringid:
deviceID--;
if (deviceID > 0)
{
for (i = 0; i < menuPlayers; i++)
{
if (deviceID != cv_usejoystick[i].value)
continue;
// Controller taken? Try again...
goto loweringid;
}
goto retrygetcontrol;
}
if (trydefaults && G_KeyBindIsNecessary(gc))
{
// If we still haven't found anything and the keybind is necessary,
// try it all again but with default binds.
trydefaults = false;
controltable = &(gamecontroldefault[gc][0]);
tryingotherID = false;
deviceID = cv_usejoystick[p].value;
goto retrygetcontrol;
return value;
}
}
// Literally not bound at all, so it can't be pressed at all.
return 0;
}
@ -1535,7 +1530,7 @@ void G_DoLoadLevelEx(boolean resetplayer, gamestate_t newstate)
// 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();
@ -1855,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,9 @@
#include "d_net.h"
#include "console.h"
#include "i_joy.h" // JOYAXISRANGE
#include "r_draw.h" // GTC_ macros for assigning gamepad indicator colors
#include "v_video.h" // V_GetColor for assigning gamepad indictaor colors
#include "z_zone.h"
#define MAXMOUSESENSITIVITY 100 // sensitivity steps
@ -35,11 +38,11 @@ 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];
INT32 gamecontroldefault[num_gamecontrols][MAXINPUTMAPPING]; // default control storage
INT32 menucontrolreserved[num_gamecontrols][MAXINPUTMAPPING];
// lists of GC codes for selective operation
/*
@ -68,13 +71,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 +155,203 @@ 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);
}
}
}
}
void G_SetPlayerGamepadIndicatorToPlayerColor(INT32 player)
{
INT32 device;
INT32 skin;
UINT16 skincolor;
UINT8 *colormap;
byteColor_t byte_color;
I_Assert(player >= 0 && player < MAXSPLITSCREENPLAYERS);
device = G_GetDeviceForPlayer(player);
if (device <= 0)
{
return;
}
skin = cv_skin[player].value;
skincolor = cv_playercolor[player].value;
colormap = R_GetTranslationColormap(skin, skincolor, GTC_MENUCACHE);
if (colormap == NULL)
{
return;
}
byte_color = V_GetColor(colormap[104]).s;
I_SetGamepadIndicatorColor(device, byte_color.red, byte_color.green, byte_color.blue);
}
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 +363,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 +388,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 +410,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 +425,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 +468,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 +484,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 +500,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);
}
}
}
@ -405,12 +683,13 @@ boolean G_KeyBindIsNecessary(INT32 gc)
switch (gc)
{
case gc_a:
case gc_b:
case gc_c:
case gc_x:
case gc_up:
case gc_down:
case gc_left:
case gc_right:
case gc_start:
//case gc_start: // Is necessary, but handled special.
return true;
default:
return false;
@ -421,25 +700,31 @@ boolean G_KeyBindIsNecessary(INT32 gc)
// Returns false if a key is deemed unreachable for this device.
boolean G_KeyIsAvailable(INT32 key, INT32 deviceID)
{
boolean gamepad_key = false;
// Invalid key number.
if (key <= 0 || key >= NUMINPUTS)
{
return false;
}
// Valid controller-specific virtual key, but no controller attached for player.
if (key >= KEY_JOY1 && key < JOYINPUTEND && deviceID <= 0)
// Only allow gamepad keys for gamepad devices,
// and vice versa.
gamepad_key = (key >= KEY_JOY1 && key < JOYINPUTEND);
if (deviceID == KEYBOARD_MOUSE_DEVICE)
{
if (gamepad_key == true)
{
return false;
}
// Valid mouse-specific virtual key, but no mouse attached for player. TODO HOW TO DETECT ACTIVE MOUSE CONNECTION
/*
if (key >= KEY_MOUSE1 && key < MOUSEINPUTEND && ????????)
}
else
{
if (gamepad_key == false)
{
return false;
}
*/
}
return true;
}
@ -538,7 +823,7 @@ void G_DefineDefaultControls(void)
gamecontroldefault[gc_z ][0] = 'd';
gamecontroldefault[gc_l ][0] = 'q';
gamecontroldefault[gc_r ][0] = 'e';
gamecontroldefault[gc_start ][0] = KEY_ESCAPE; // *
gamecontroldefault[gc_start ][0] = KEY_ESCAPE;
gamecontroldefault[gc_rankings][0] = KEY_TAB;
// Gamepad controls
@ -560,6 +845,16 @@ void G_DefineDefaultControls(void)
gamecontroldefault[gc_down ][2] = KEY_AXIS1+3; // Axis Y+
gamecontroldefault[gc_left ][2] = KEY_AXIS1+0; // Axis X-
gamecontroldefault[gc_right][2] = KEY_AXIS1+1; // Axis X+
// Menu reserved controls
menucontrolreserved[gc_up ][0] = KEY_UPARROW;
menucontrolreserved[gc_down ][0] = KEY_DOWNARROW;
menucontrolreserved[gc_left ][0] = KEY_LEFTARROW;
menucontrolreserved[gc_right][0] = KEY_RIGHTARROW;
menucontrolreserved[gc_a ][0] = KEY_ENTER;
menucontrolreserved[gc_c ][0] = KEY_BACKSPACE;
menucontrolreserved[gc_x ][0] = KEY_ESCAPE;
menucontrolreserved[gc_start][0] = KEY_ESCAPE; // Handled special
}
void G_CopyControls(INT32 (*setupcontrols)[MAXINPUTMAPPING], INT32 (*fromcontrols)[MAXINPUTMAPPING], const INT32 *gclist, INT32 gclen)

View file

@ -107,12 +107,14 @@ 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)
#define UNASSIGNED_DEVICE (-1)
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];
extern INT32 gamecontroldefault[num_gamecontrols][MAXINPUTMAPPING]; // default control storage
extern INT32 menucontrolreserved[num_gamecontrols][MAXINPUTMAPPING];
/*
#define num_gcl_accelerate 1
@ -135,7 +137,33 @@ 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);
void G_SetPlayerGamepadIndicatorToPlayerColor(INT32 player);
/// 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

@ -239,6 +239,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,38 @@ 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 (device == KEYBOARD_MOUSE_DEVICE && num != 0)
{
// Only player 1 can be assigned to the KBM device.
continue;
}
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 +613,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 +635,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 +687,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);
G_SetDeviceForPlayer(num, -1);
CONS_Printf("M_HandleCSelectProfile: Device for %d set to %d\n", num, -1);
}
return true;
}
@ -1478,16 +1494,12 @@ void M_CharacterSelectTick(void)
else
CV_StealthSet(&cv_follower[i], followers[setup_player[i].followern].name);
CV_StealthSetValue(&cv_followercolor[i], setup_player[i].followercolor);
G_SetPlayerGamepadIndicatorToPlayerColor(i);
}
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

View file

@ -194,74 +194,9 @@ 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
\return void
*/
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;
//JoySet->scale
}
/** \brief First joystick up and running
*/
static INT32 joystick_started[MAXSPLITSCREENPLAYERS] = {0,0,0,0};
/** \brief SDL info about joystick 1
/** \brief SDL info about joysticks
*/
SDLJoyInfo_t JoyInfo[MAXSPLITSCREENPLAYERS];
SDL_GameController *ExJoystick[MAXGAMEPADS];
SDL_bool consolevent = SDL_FALSE;
SDL_bool framebuffer = SDL_FALSE;
@ -983,316 +918,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;
return;
}
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);
}
// 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_WasInit(SDL_INIT_GAMECONTROLLER) == 0)
{
if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == -1)
{
CONS_Printf(M_GetText("Couldn't initialize gamepads: %s\n"), SDL_GetError());
CONS_Printf(M_GetText("Couldn't initialize game controllers: %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 +1019,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)
{
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)
Impl_HandleControllerDeviceAddedEvent(evt.cdevice);
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);
}
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