Merge pull request #827 from protocultor/sdlcontroller

Full transition to SDL_GameController for gamepad usage
This commit is contained in:
Yamagi 2022-05-17 19:42:19 +02:00 committed by GitHub
commit 12c7fddfaa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 793 additions and 428 deletions

View File

@ -135,89 +135,59 @@ keyname_t keynames[] = {
{"MWHEELUP", K_MWHEELUP},
{"MWHEELDOWN", K_MWHEELDOWN},
{"JOY1", K_JOY1},
{"JOY2", K_JOY2},
{"JOY3", K_JOY3},
{"JOY4", K_JOY4},
{"JOY5", K_JOY5},
{"JOY6", K_JOY6},
{"JOY7", K_JOY7},
{"JOY8", K_JOY8},
{"JOY9", K_JOY9},
{"JOY10", K_JOY10},
{"JOY11", K_JOY11},
{"JOY12", K_JOY12},
{"JOY13", K_JOY13},
{"JOY14", K_JOY14},
{"JOY15", K_JOY15},
{"JOY16", K_JOY16},
{"JOY17", K_JOY17},
{"JOY18", K_JOY18},
{"JOY19", K_JOY19},
{"JOY20", K_JOY20},
{"JOY21", K_JOY21},
{"JOY22", K_JOY22},
{"JOY23", K_JOY23},
{"JOY24", K_JOY24},
{"JOY25", K_JOY25},
{"JOY26", K_JOY26},
{"JOY27", K_JOY27},
{"JOY28", K_JOY28},
{"JOY29", K_JOY29},
{"JOY30", K_JOY30},
{"JOY31", K_JOY31},
{"JOY32", K_JOY32},
{"HAT_UP", K_HAT_UP},
{"HAT_RIGHT", K_HAT_RIGHT},
{"HAT_DOWN", K_HAT_DOWN},
{"HAT_LEFT", K_HAT_LEFT},
{"BTN_A", K_BTN_A},
{"BTN_B", K_BTN_B},
{"BTN_X", K_BTN_X},
{"BTN_Y", K_BTN_Y},
{"STICK_LEFT", K_STICK_LEFT},
{"STICK_RIGHT", K_STICK_RIGHT},
{"SHOULDR_LEFT", K_SHOULDER_LEFT},
{"SHOULDR_RIGHT", K_SHOULDER_RIGHT},
{"TRIG_LEFT", K_TRIG_LEFT},
{"TRIG_RIGHT", K_TRIG_RIGHT},
{"DP_UP", K_DPAD_UP},
{"DP_DOWN", K_DPAD_DOWN},
{"DP_LEFT", K_DPAD_LEFT},
{"DP_RIGHT", K_DPAD_RIGHT},
{"PADDLE_1", K_PADDLE_1},
{"PADDLE_2", K_PADDLE_2},
{"PADDLE_3", K_PADDLE_3},
{"PADDLE_4", K_PADDLE_4},
{"BTN_MISC1", K_BTN_MISC1},
{"TOUCHPAD", K_TOUCHPAD},
{"BTN_BACK", K_BTN_BACK},
{"BTN_GUIDE", K_BTN_GUIDE},
{"BTN_START", K_BTN_START},
// virtual keys you get by pressing the corresponding normal joy key
// and the altselector key
{"JOY1_ALT", K_JOY1_ALT},
{"JOY2_ALT", K_JOY2_ALT},
{"JOY3_ALT", K_JOY3_ALT},
{"JOY4_ALT", K_JOY4_ALT},
{"JOY5_ALT", K_JOY5_ALT},
{"JOY6_ALT", K_JOY6_ALT},
{"JOY7_ALT", K_JOY7_ALT},
{"JOY8_ALT", K_JOY8_ALT},
{"JOY9_ALT", K_JOY9_ALT},
{"JOY10_ALT", K_JOY10_ALT},
{"JOY11_ALT", K_JOY11_ALT},
{"JOY12_ALT", K_JOY12_ALT},
{"JOY13_ALT", K_JOY13_ALT},
{"JOY14_ALT", K_JOY14_ALT},
{"JOY15_ALT", K_JOY15_ALT},
{"JOY16_ALT", K_JOY16_ALT},
{"JOY17_ALT", K_JOY17_ALT},
{"JOY18_ALT", K_JOY18_ALT},
{"JOY19_ALT", K_JOY19_ALT},
{"JOY20_ALT", K_JOY20_ALT},
{"JOY21_ALT", K_JOY21_ALT},
{"JOY22_ALT", K_JOY22_ALT},
{"JOY23_ALT", K_JOY23_ALT},
{"JOY24_ALT", K_JOY24_ALT},
{"JOY25_ALT", K_JOY25_ALT},
{"JOY26_ALT", K_JOY26_ALT},
{"JOY27_ALT", K_JOY27_ALT},
{"JOY28_ALT", K_JOY28_ALT},
{"JOY29_ALT", K_JOY29_ALT},
{"JOY30_ALT", K_JOY30_ALT},
{"JOY31_ALT", K_JOY31_ALT},
{"JOY32_ALT", K_JOY32_ALT},
{"BTN_A_ALT", K_BTN_A_ALT},
{"BTN_B_ALT", K_BTN_B_ALT},
{"BTN_X_ALT", K_BTN_X_ALT},
{"BTN_Y_ALT", K_BTN_Y_ALT},
{"STICK_LEFT_ALT", K_STICK_LEFT_ALT},
{"STICK_RIGHT_ALT", K_STICK_RIGHT_ALT},
{"SHOULDR_LEFT_ALT", K_SHOULDER_LEFT_ALT},
{"SHOULDR_RIGHT_ALT", K_SHOULDER_RIGHT_ALT},
{"TRIG_LEFT_ALT", K_TRIG_LEFT_ALT},
{"TRIG_RIGHT_ALT", K_TRIG_RIGHT_ALT},
{"HAT_UP_ALT", K_HAT_UP_ALT},
{"HAT_RIGHT_ALT", K_HAT_RIGHT_ALT},
{"HAT_DOWN_ALT", K_HAT_DOWN_ALT},
{"HAT_LEFT_ALT", K_HAT_LEFT_ALT},
{"DP_UP_ALT", K_DPAD_UP_ALT},
{"DP_DOWN_ALT", K_DPAD_DOWN_ALT},
{"DP_LEFT_ALT", K_DPAD_LEFT_ALT},
{"DP_RIGHT_ALT", K_DPAD_RIGHT_ALT},
{"TRIG_LEFT", K_TRIG_LEFT_ALT},
{"TRIG_RIGHT", K_TRIG_RIGHT_ALT},
{"PADDLE_1_ALT", K_PADDLE_1_ALT},
{"PADDLE_2_ALT", K_PADDLE_2_ALT},
{"PADDLE_3_ALT", K_PADDLE_3_ALT},
{"PADDLE_4_ALT", K_PADDLE_4_ALT},
{"BTN_MISC1_ALT", K_BTN_MISC1_ALT},
{"TOUCHPAD_ALT", K_TOUCHPAD_ALT},
{"BTN_BACK_ALT", K_BTN_BACK_ALT},
{"BTN_GUIDE_ALT", K_BTN_GUIDE_ALT},
{"BTN_START_ALT", K_BTN_START_ALT},
{"JOY_BACK", K_JOY_BACK},
@ -1194,13 +1164,13 @@ Key_Event(int key, qboolean down, qboolean special)
cvar_t *fullscreen;
unsigned int time = Sys_Milliseconds();
// evil hack for the joystick key altselector, which turns K_JOYx into K_JOYx_ALT
if(joy_altselector_pressed && key >= K_JOY1 && key <= K_JOY_LAST_REGULAR)
// evil hack for the joystick key altselector, which turns K_BTN_x into K_BTN_x_ALT
if(joy_altselector_pressed && key >= K_JOY_FIRST_REGULAR && key <= K_JOY_LAST_REGULAR)
{
// make sure key is not the altselector itself (which we won't turn into *_ALT)
if(keybindings[key] == NULL || strcmp(keybindings[key], "+joyaltselector") != 0)
{
int altkey = key + (K_JOY1_ALT - K_JOY1);
int altkey = key + (K_JOY_FIRST_REGULAR_ALT - K_JOY_FIRST_REGULAR);
// allow fallback to binding with non-alt key
if(keybindings[altkey] != NULL || keybindings[key] == NULL)
key = altkey;

View File

@ -42,7 +42,7 @@
/*
* the joystick altselector key is pressed
* => K_JOYx turns into K_JOYx_ALT
* => K_BTN_x turns into K_BTN_x_ALT
*/
extern qboolean joy_altselector_pressed;
@ -125,98 +125,6 @@ enum QKEYS {
K_MWHEELDOWN,
K_MWHEELUP,
K_JOY1,
K_JOY2,
K_JOY3,
K_JOY4,
K_JOY5,
K_JOY6,
K_JOY7,
K_JOY8,
K_JOY9,
K_JOY10,
K_JOY11,
K_JOY12,
K_JOY13,
K_JOY14,
K_JOY15,
K_JOY16,
K_JOY17,
K_JOY18,
K_JOY19,
K_JOY20,
K_JOY21,
K_JOY22,
K_JOY23,
K_JOY24,
K_JOY25,
K_JOY26,
K_JOY27,
K_JOY28,
K_JOY29,
K_JOY30,
K_JOY31,
K_JOY32,
K_HAT_UP,
K_HAT_RIGHT,
K_HAT_DOWN,
K_HAT_LEFT,
K_TRIG_LEFT,
K_TRIG_RIGHT,
// add other joystick/controller keys before this one
// and adjust it accordingly, also remember to add corresponding _ALT key below!
K_JOY_LAST_REGULAR = K_TRIG_RIGHT,
/* Can't be mapped to any action (=> not regular) */
K_JOY_BACK,
K_JOY1_ALT,
K_JOY2_ALT,
K_JOY3_ALT,
K_JOY4_ALT,
K_JOY5_ALT,
K_JOY6_ALT,
K_JOY7_ALT,
K_JOY8_ALT,
K_JOY9_ALT,
K_JOY10_ALT,
K_JOY11_ALT,
K_JOY12_ALT,
K_JOY13_ALT,
K_JOY14_ALT,
K_JOY15_ALT,
K_JOY16_ALT,
K_JOY17_ALT,
K_JOY18_ALT,
K_JOY19_ALT,
K_JOY20_ALT,
K_JOY21_ALT,
K_JOY22_ALT,
K_JOY23_ALT,
K_JOY24_ALT,
K_JOY25_ALT,
K_JOY26_ALT,
K_JOY27_ALT,
K_JOY28_ALT,
K_JOY29_ALT,
K_JOY30_ALT,
K_JOY31_ALT,
K_JOY32_ALT,
K_HAT_UP_ALT,
K_HAT_RIGHT_ALT,
K_HAT_DOWN_ALT,
K_HAT_LEFT_ALT,
K_TRIG_LEFT_ALT,
K_TRIG_RIGHT_ALT,
// add other joystick/controller keys before this one and adjust it accordingly
K_JOY_LAST_REGULAR_ALT = K_TRIG_RIGHT_ALT,
K_SUPER, // TODO: what is this? SDL doesn't seem to know it..
K_COMPOSE,
K_MODE,
@ -296,6 +204,67 @@ enum QKEYS {
// want to be able to type in the console) - the user can't bind this key.
K_CONSOLE,
// Keyboard keys / codes end here. Any new ones should go before this.
// From here on, only gamepad controls must be allowed.
// Otherwise, separate bindings (keyboard / controller) menu options will not work.
K_BTN_A,
K_JOY_FIRST_REGULAR = K_BTN_A,
K_BTN_B,
K_BTN_X,
K_BTN_Y,
K_BTN_BACK,
K_BTN_GUIDE,
K_BTN_START,
K_STICK_LEFT,
K_STICK_RIGHT,
K_SHOULDER_LEFT,
K_SHOULDER_RIGHT,
K_TRIG_LEFT,
K_TRIG_RIGHT,
K_DPAD_UP,
K_DPAD_DOWN,
K_DPAD_LEFT,
K_DPAD_RIGHT,
K_BTN_MISC1,
K_PADDLE_1,
K_PADDLE_2,
K_PADDLE_3,
K_PADDLE_4,
K_TOUCHPAD,
// add other joystick/controller keys before this one
// and adjust it accordingly, also remember to add corresponding _ALT key below!
K_JOY_LAST_REGULAR = K_TOUCHPAD,
/* Can't be mapped to any action (=> not regular) */
K_JOY_BACK,
K_BTN_A_ALT,
K_JOY_FIRST_REGULAR_ALT = K_BTN_A_ALT,
K_BTN_B_ALT,
K_BTN_X_ALT,
K_BTN_Y_ALT,
K_BTN_BACK_ALT,
K_BTN_GUIDE_ALT,
K_BTN_START_ALT,
K_STICK_LEFT_ALT,
K_STICK_RIGHT_ALT,
K_SHOULDER_LEFT_ALT,
K_SHOULDER_RIGHT_ALT,
K_TRIG_LEFT_ALT,
K_TRIG_RIGHT_ALT,
K_DPAD_UP_ALT,
K_DPAD_DOWN_ALT,
K_DPAD_LEFT_ALT,
K_DPAD_RIGHT_ALT,
K_BTN_MISC1_ALT,
K_PADDLE_1_ALT,
K_PADDLE_2_ALT,
K_PADDLE_3_ALT,
K_PADDLE_4_ALT,
K_TOUCHPAD_ALT,
K_LAST
};

View File

@ -47,7 +47,6 @@
// IN_Update() called at the beginning of a frame to the
// actual movement functions called at a later time.
static float mouse_x, mouse_y;
static int back_button_id = -1;
static int sdl_back_button = SDL_CONTROLLER_BUTTON_BACK;
static float joystick_yaw, joystick_pitch;
static float joystick_forwardmove, joystick_sidemove;
@ -58,7 +57,7 @@ static qboolean mlooking;
// Used throughout the client.
int sys_frame_time;
// the joystick altselector that turns K_JOYX into K_JOYX_ALT
// the joystick altselector that turns K_BTN_X into K_BTN_X_ALT
// is pressed
qboolean joy_altselector_pressed = false;
@ -95,7 +94,6 @@ struct hapric_effects_cache {
qboolean show_haptic;
static SDL_Haptic *joystick_haptic = NULL;
static SDL_Joystick *joystick = NULL;
static SDL_GameController *controller = NULL;
#define HAPTIC_EFFECT_LIST_SIZE 16
@ -132,6 +130,10 @@ static cvar_t *joy_axis_triggerright_threshold;
// Joystick haptic
static cvar_t *joy_haptic_magnitude;
// Support for hot plugging of game controller
static qboolean first_init = true;
static int init_delay = 30;
/* ------------------------------------------------------------------ */
/*
@ -421,6 +423,48 @@ IN_TranslateScancodeToQ2Key(SDL_Scancode sc)
return 0;
}
static int
IN_TranslateGamepadBtnToQ2Key(int btn)
{
#define MY_BTN_CASE(X,Y) case SDL_CONTROLLER_BUTTON_ ## X : return K_ ## Y;
switch( btn )
{
// case SDL_CONTROLLER_BUTTON_A : return K_BTN_A;
MY_BTN_CASE(A,BTN_A)
MY_BTN_CASE(B,BTN_B)
MY_BTN_CASE(X,BTN_X)
MY_BTN_CASE(Y,BTN_Y)
MY_BTN_CASE(LEFTSHOULDER,SHOULDER_LEFT)
MY_BTN_CASE(RIGHTSHOULDER,SHOULDER_RIGHT)
MY_BTN_CASE(LEFTSTICK,STICK_LEFT)
MY_BTN_CASE(RIGHTSTICK,STICK_RIGHT)
MY_BTN_CASE(DPAD_UP,DPAD_UP)
MY_BTN_CASE(DPAD_DOWN,DPAD_DOWN)
MY_BTN_CASE(DPAD_LEFT,DPAD_LEFT)
MY_BTN_CASE(DPAD_RIGHT,DPAD_RIGHT)
#if SDL_VERSION_ATLEAST(2, 0, 14) // support for newer buttons
MY_BTN_CASE(PADDLE1,PADDLE_1)
MY_BTN_CASE(PADDLE2,PADDLE_2)
MY_BTN_CASE(PADDLE3,PADDLE_3)
MY_BTN_CASE(PADDLE4,PADDLE_4)
MY_BTN_CASE(MISC1,BTN_MISC1)
MY_BTN_CASE(TOUCHPAD,TOUCHPAD)
#endif
MY_BTN_CASE(BACK,BTN_BACK)
MY_BTN_CASE(GUIDE,BTN_GUIDE)
MY_BTN_CASE(START,BTN_START)
}
#undef MY_BTN_CASE
return 0;
}
static void IN_Controller_Init(qboolean notify_user);
static void IN_Controller_Shutdown(qboolean notify_user);
/* ------------------------------------------------------------------ */
/*
@ -435,7 +479,6 @@ IN_Update(void)
SDL_Event event;
unsigned int key;
static char last_hat = SDL_HAT_CENTERED;
static qboolean left_trigger = false;
static qboolean right_trigger = false;
@ -583,13 +626,21 @@ IN_Update(void)
break;
case SDL_CONTROLLERBUTTONUP:
case SDL_CONTROLLERBUTTONDOWN: /* Handle Controller Back button */
case SDL_CONTROLLERBUTTONDOWN:
{
qboolean down = (event.type == SDL_CONTROLLERBUTTONDOWN);
// Handle Back Button first, to override its original key
if (event.cbutton.button == sdl_back_button)
{
Key_Event(K_JOY_BACK, down, true);
break;
}
key = IN_TranslateGamepadBtnToQ2Key(event.cbutton.button);
if(key != 0)
{
Key_Event(key, down, true);
}
break;
@ -727,57 +778,24 @@ IN_Update(void)
break;
}
// Joystick can have more buttons than on general game controller
// so try to map not free buttons
case SDL_JOYBUTTONUP:
case SDL_JOYBUTTONDOWN:
{
qboolean down = (event.type == SDL_JOYBUTTONDOWN);
// Ignore back button, we don't need event for such button
if (back_button_id == event.jbutton.button)
case SDL_CONTROLLERDEVICEREMOVED:
if (!controller)
{
return;
break;
}
if (event.jbutton.button <= (K_JOY32 - K_JOY1))
{
Key_Event(event.jbutton.button + K_JOY1, down, true);
if (event.cdevice.which == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller))) {
IN_Controller_Shutdown(true);
IN_Controller_Init(false);
}
break;
}
case SDL_JOYHATMOTION:
{
if (last_hat != event.jhat.value)
case SDL_JOYDEVICEADDED:
if (!controller)
{
char diff = last_hat ^event.jhat.value;
int i;
for (i = 0; i < 4; i++)
{
if (diff & (1 << i))
{
// check that we have button up for some bit
if (last_hat & (1 << i))
{
Key_Event(i + K_HAT_UP, false, true);
}
/* check that we have button down for some bit */
if (event.jhat.value & (1 << i))
{
Key_Event(i + K_HAT_UP, true, true);
}
}
}
last_hat = event.jhat.value;
// This should be lower, but some controllers just don't want to get detected by the OS
init_delay = 100;
}
break;
}
case SDL_QUIT:
Com_Quit();
@ -803,6 +821,26 @@ IN_Update(void)
// We need to save the frame time so other subsystems
// know the exact time of the last input events.
sys_frame_time = Sys_Milliseconds();
// Hot plugging delay handling, to not be "overwhelmed" because some controllers
// present themselves as two different devices, triggering SDL_JOYDEVICEADDED
// too many times. They could trigger it even at game initialization.
if (init_delay)
{
init_delay--;
if (!init_delay)
{
if (!first_init)
{
IN_Controller_Shutdown(false);
IN_Controller_Init(true);
}
else
{
first_init = false;
}
}
}
}
/*
@ -1021,7 +1059,7 @@ IN_Haptic_Effects_Info(void)
{
show_haptic = true;
Com_Printf ("Joystic/Mouse haptic:\n");
Com_Printf ("Joystick/Mouse haptic:\n");
Com_Printf (" * %d effects\n", SDL_HapticNumEffects(joystick_haptic));
Com_Printf (" * %d effects in same time\n", SDL_HapticNumEffectsPlaying(joystick_haptic));
Com_Printf (" * %d haptic axis\n", SDL_HapticNumAxes(joystick_haptic));
@ -1195,15 +1233,157 @@ Haptic_Feedback(char *name, int effect_volume, int effect_duration,
}
}
/*
* Game Controller
*/
static void
IN_Controller_Init(qboolean notify_user)
{
cvar_t *in_sdlbackbutton;
int nummappings;
char controllerdb[MAX_OSPATH] = {0};
SDL_Joystick *joystick = NULL;
SDL_bool is_controller = SDL_FALSE;
in_sdlbackbutton = Cvar_Get("in_sdlbackbutton", "0", CVAR_ARCHIVE);
if (in_sdlbackbutton)
{
switch ((int)in_sdlbackbutton->value)
{
case 1:
sdl_back_button = SDL_CONTROLLER_BUTTON_START;
break;
case 2:
sdl_back_button = SDL_CONTROLLER_BUTTON_GUIDE;
break;
default:
sdl_back_button = SDL_CONTROLLER_BUTTON_BACK;
}
}
if (notify_user)
{
Com_Printf("- Game Controller init attempt -\n");
}
if (!SDL_WasInit(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC))
{
if (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) == -1)
{
Com_Printf ("Couldn't init SDL joystick: %s.\n", SDL_GetError ());
return;
}
}
Com_Printf ("%i joysticks were found.\n", SDL_NumJoysticks());
if (!SDL_NumJoysticks())
{
joystick_haptic = SDL_HapticOpenFromMouse();
if (joystick_haptic == NULL)
{
Com_Printf("Most likely mouse isn't haptic.\n");
}
else
{
IN_Haptic_Effects_Info();
}
return;
}
for (const char* rawPath = FS_GetNextRawPath(NULL); rawPath != NULL; rawPath = FS_GetNextRawPath(rawPath))
{
snprintf(controllerdb, MAX_OSPATH, "%s/gamecontrollerdb.txt", rawPath);
nummappings = SDL_GameControllerAddMappingsFromFile(controllerdb);
if (nummappings > 0)
Com_Printf ("%d mappings loaded from gamecontrollerdb.txt\n", nummappings);
}
for (int i = 0; i < SDL_NumJoysticks(); i++)
{
joystick = SDL_JoystickOpen(i);
const char* joystick_name = SDL_JoystickName(joystick);
const int name_len = strlen(joystick_name);
Com_Printf ("The name of the joystick is '%s'\n", joystick_name);
// Ugly hack to detect IMU-only devices - works for Switch Pro Controller at least
if (name_len > 4 && !strncmp(joystick_name + name_len - 4, " IMU", 4))
{
Com_Printf ("Skipping IMU device.\n");
SDL_JoystickClose(joystick);
joystick = NULL;
continue;
}
Com_Printf ("Number of Axes: %d\n", SDL_JoystickNumAxes(joystick));
Com_Printf ("Number of Buttons: %d\n", SDL_JoystickNumButtons(joystick));
Com_Printf ("Number of Balls: %d\n", SDL_JoystickNumBalls(joystick));
Com_Printf ("Number of Hats: %d\n", SDL_JoystickNumHats(joystick));
is_controller = SDL_IsGameController(i);
if (!is_controller)
{
char joystick_guid[256] = {0};
SDL_JoystickGUID guid;
guid = SDL_JoystickGetDeviceGUID(i);
SDL_JoystickGetGUIDString(guid, joystick_guid, 255);
Com_Printf ("To use joystick as game controller please set SDL_GAMECONTROLLERCONFIG:\n");
Com_Printf ("e.g.: SDL_GAMECONTROLLERCONFIG='%s,%s,leftx:a0,lefty:a1,rightx:a2,righty:a3,back:b1,...\n", joystick_guid, joystick_name);
Com_Printf ("Or you can put 'gamecontrollerdb.txt' in your game directory.\n");
}
SDL_JoystickClose(joystick);
joystick = NULL;
if (is_controller)
{
controller = SDL_GameControllerOpen(i);
Com_Printf ("Controller settings: %s\n", SDL_GameControllerMapping(controller));
Com_Printf ("Controller axis: \n");
Com_Printf (" * leftx = %s\n", joy_axis_leftx->string);
Com_Printf (" * lefty = %s\n", joy_axis_lefty->string);
Com_Printf (" * rightx = %s\n", joy_axis_rightx->string);
Com_Printf (" * righty = %s\n", joy_axis_righty->string);
Com_Printf (" * triggerleft = %s\n", joy_axis_triggerleft->string);
Com_Printf (" * triggerright = %s\n", joy_axis_triggerright->string);
Com_Printf ("Controller thresholds: \n");
Com_Printf (" * leftx = %f\n", joy_axis_leftx_threshold->value);
Com_Printf (" * lefty = %f\n", joy_axis_lefty_threshold->value);
Com_Printf (" * rightx = %f\n", joy_axis_rightx_threshold->value);
Com_Printf (" * righty = %f\n", joy_axis_righty_threshold->value);
Com_Printf (" * triggerleft = %f\n", joy_axis_triggerleft_threshold->value);
Com_Printf (" * triggerright = %f\n", joy_axis_triggerright_threshold->value);
joystick_haptic = SDL_HapticOpenFromJoystick(SDL_GameControllerGetJoystick(controller));
if (joystick_haptic == NULL)
{
Com_Printf("Most likely controller isn't haptic.\n");
}
else
{
IN_Haptic_Effects_Info();
}
break;
}
}
}
/*
* Initializes the backend
*/
void
IN_Init(void)
{
cvar_t *in_sdlbackbutton;
int nummappings;
char controllerdb[MAX_OSPATH] = {0};
Com_Printf("------- input initialization -------\n");
mouse_x = mouse_y = 0;
@ -1246,22 +1426,6 @@ IN_Init(void)
windowed_mouse = Cvar_Get("windowed_mouse", "1", CVAR_USERINFO | CVAR_ARCHIVE);
in_sdlbackbutton = Cvar_Get("in_sdlbackbutton", "0", CVAR_ARCHIVE);
if (in_sdlbackbutton)
{
switch ((int)in_sdlbackbutton->value)
{
case 1:
sdl_back_button = SDL_CONTROLLER_BUTTON_START;
break;
case 2:
sdl_back_button = SDL_CONTROLLER_BUTTON_GUIDE;
break;
default:
sdl_back_button = SDL_CONTROLLER_BUTTON_BACK;
}
}
Cmd_AddCommand("+mlook", IN_MLookDown);
Cmd_AddCommand("-mlook", IN_MLookUp);
@ -1270,108 +1434,7 @@ IN_Init(void)
SDL_StartTextInput();
/* Joystick init */
if (!SDL_WasInit(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC))
{
if (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) == -1)
{
Com_Printf ("Couldn't init SDL joystick: %s.\n", SDL_GetError ());
}
else
{
Com_Printf ("%i joysticks were found.\n", SDL_NumJoysticks());
if (SDL_NumJoysticks() > 0)
{
for (const char* rawPath = FS_GetNextRawPath(NULL); rawPath != NULL; rawPath = FS_GetNextRawPath(rawPath))
{
snprintf(controllerdb, MAX_OSPATH, "%s/gamecontrollerdb.txt", rawPath);
nummappings = SDL_GameControllerAddMappingsFromFile(controllerdb);
if (nummappings > 0)
Com_Printf ("%d mappings loaded from gamecontrollerdb.txt\n", nummappings);
}
for (int i = 0; i < SDL_NumJoysticks(); i++) {
joystick = SDL_JoystickOpen(i);
Com_Printf ("The name of the joystick is '%s'\n", SDL_JoystickName(joystick));
Com_Printf ("Number of Axes: %d\n", SDL_JoystickNumAxes(joystick));
Com_Printf ("Number of Buttons: %d\n", SDL_JoystickNumButtons(joystick));
Com_Printf ("Number of Balls: %d\n", SDL_JoystickNumBalls(joystick));
Com_Printf ("Number of Hats: %d\n", SDL_JoystickNumHats(joystick));
joystick_haptic = SDL_HapticOpenFromJoystick(joystick);
if (joystick_haptic == NULL)
{
Com_Printf("Most likely joystick isn't haptic.\n");
}
else
{
IN_Haptic_Effects_Info();
}
if(SDL_IsGameController(i))
{
SDL_GameControllerButtonBind backBind;
controller = SDL_GameControllerOpen(i);
Com_Printf ("Controller settings: %s\n", SDL_GameControllerMapping(controller));
Com_Printf ("Controller axis: \n");
Com_Printf (" * leftx = %s\n", joy_axis_leftx->string);
Com_Printf (" * lefty = %s\n", joy_axis_lefty->string);
Com_Printf (" * rightx = %s\n", joy_axis_rightx->string);
Com_Printf (" * righty = %s\n", joy_axis_righty->string);
Com_Printf (" * triggerleft = %s\n", joy_axis_triggerleft->string);
Com_Printf (" * triggerright = %s\n", joy_axis_triggerright->string);
Com_Printf ("Controller thresholds: \n");
Com_Printf (" * leftx = %f\n", joy_axis_leftx_threshold->value);
Com_Printf (" * lefty = %f\n", joy_axis_lefty_threshold->value);
Com_Printf (" * rightx = %f\n", joy_axis_rightx_threshold->value);
Com_Printf (" * righty = %f\n", joy_axis_righty_threshold->value);
Com_Printf (" * triggerleft = %f\n", joy_axis_triggerleft_threshold->value);
Com_Printf (" * triggerright = %f\n", joy_axis_triggerright_threshold->value);
backBind = SDL_GameControllerGetBindForButton(controller, sdl_back_button);
if (backBind.bindType == SDL_CONTROLLER_BINDTYPE_BUTTON)
{
back_button_id = backBind.value.button;
Com_Printf ("\nBack button JOY%d will be unbindable.\n", back_button_id+1);
}
break;
}
else
{
char joystick_guid[256] = {0};
SDL_JoystickGUID guid;
guid = SDL_JoystickGetDeviceGUID(i);
SDL_JoystickGetGUIDString(guid, joystick_guid, 255);
Com_Printf ("To use joystick as game controller please set SDL_GAMECONTROLLERCONFIG:\n");
Com_Printf ("e.g.: SDL_GAMECONTROLLERCONFIG='%s,%s,leftx:a0,lefty:a1,rightx:a2,righty:a3,back:b1,...\n", joystick_guid, SDL_JoystickName(joystick));
}
}
}
else
{
joystick_haptic = SDL_HapticOpenFromMouse();
if (joystick_haptic == NULL)
{
Com_Printf("Most likely mouse isn't haptic.\n");
}
else
{
IN_Haptic_Effects_Info();
}
}
}
}
IN_Controller_Init(false);
Com_Printf("------------------------------------\n\n");
}
@ -1391,6 +1454,23 @@ IN_Haptic_Shutdown(void)
}
}
static void
IN_Controller_Shutdown(qboolean notify_user)
{
if (notify_user)
{
Com_Printf("- Game Controller disconnected -\n");
}
IN_Haptic_Shutdown();
if (controller)
{
SDL_GameControllerClose(controller);
controller = NULL;
}
}
void
IN_Shutdown(void)
{
@ -1400,20 +1480,7 @@ IN_Shutdown(void)
Com_Printf("Shutting down input.\n");
IN_Haptic_Shutdown();
if (controller)
{
back_button_id = -1;
SDL_GameControllerClose(controller);
controller = NULL;
}
if (joystick)
{
SDL_JoystickClose(joystick);
joystick = NULL;
}
IN_Controller_Shutdown(false);
const Uint32 subsystems = SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC;
if (SDL_WasInit(subsystems) == subsystems)

View File

@ -43,6 +43,13 @@
#define QMF_GRAYED 0x00000002
#define QMF_NUMBERSONLY 0x00000004
enum {
KEYS_ALL = 0,
KEYS_KEYBOARD_MOUSE,
KEYS_CONTROLLER,
KEYS_CONTROLLER_ALT
};
typedef struct _tag_menuframework
{
int x, y;

View File

@ -64,6 +64,8 @@ static void M_Menu_Video_f(void);
static void M_Menu_Options_f(void);
static void M_Menu_Keys_f(void);
static void M_Menu_Joy_f(void);
static void M_Menu_ControllerButtons_f(void);
static void M_Menu_ControllerAltButtons_f(void);
static void M_Menu_Quit_f(void);
void M_Menu_Credits(void);
@ -229,25 +231,25 @@ Key_GetMenuKey(int key)
{
case K_KP_UPARROW:
case K_UPARROW:
case K_HAT_UP:
case K_DPAD_UP:
return K_UPARROW;
case K_TAB:
case K_KP_DOWNARROW:
case K_DOWNARROW:
case K_HAT_DOWN:
case K_DPAD_DOWN:
return K_DOWNARROW;
case K_KP_LEFTARROW:
case K_LEFTARROW:
case K_HAT_LEFT:
case K_TRIG_LEFT:
case K_DPAD_LEFT:
case K_SHOULDER_LEFT:
return K_LEFTARROW;
case K_KP_RIGHTARROW:
case K_RIGHTARROW:
case K_HAT_RIGHT:
case K_TRIG_RIGHT:
case K_DPAD_RIGHT:
case K_SHOULDER_RIGHT:
return K_RIGHTARROW;
case K_MOUSE1:
@ -256,45 +258,21 @@ Key_GetMenuKey(int key)
case K_MOUSE4:
case K_MOUSE5:
case K_JOY1:
case K_JOY2:
case K_JOY3:
case K_JOY4:
case K_JOY5:
case K_JOY6:
case K_JOY7:
case K_JOY8:
case K_JOY9:
case K_JOY10:
case K_JOY11:
case K_JOY12:
case K_JOY13:
case K_JOY14:
case K_JOY15:
case K_JOY16:
case K_JOY17:
case K_JOY18:
case K_JOY19:
case K_JOY20:
case K_JOY21:
case K_JOY22:
case K_JOY23:
case K_JOY24:
case K_JOY25:
case K_JOY26:
case K_JOY27:
case K_JOY28:
case K_JOY29:
case K_JOY30:
case K_JOY31:
case K_KP_ENTER:
case K_ENTER:
case K_BTN_A:
return K_ENTER;
case K_ESCAPE:
case K_JOY_BACK:
case K_BTN_B:
return K_ESCAPE;
case K_BACKSPACE:
case K_DEL:
case K_KP_DEL:
case K_BTN_Y:
return K_BACKSPACE;
}
return key;
@ -814,8 +792,7 @@ char *bindnames[][2] =
{"invdrop", "drop item"},
{"invprev", "prev item"},
{"invnext", "next item"},
{"cmd help", "help computer"},
{"+joyaltselector", "enable alt joy keys"}
{"cmd help", "help computer"}
};
#define NUM_BINDNAMES (sizeof bindnames / sizeof bindnames[0])
@ -827,14 +804,24 @@ static menuframework_s s_joy_menu;
static menuaction_s s_keys_actions[NUM_BINDNAMES];
static void
M_UnbindCommand(char *command)
M_UnbindCommand(char *command, int scope)
{
int j;
int l;
int begin = 0, end = K_LAST;
switch (scope)
{
case KEYS_KEYBOARD_MOUSE:
end = K_JOY_FIRST_REGULAR;
break;
case KEYS_CONTROLLER:
begin = K_JOY_FIRST_REGULAR;
end = K_JOY_LAST_REGULAR + 1;
break;
case KEYS_CONTROLLER_ALT:
begin = K_JOY_FIRST_REGULAR_ALT;
}
l = strlen(command);
for (j = 0; j < K_LAST; j++)
for (j = begin; j < end; j++)
{
char *b;
b = keybindings[j];
@ -844,7 +831,7 @@ M_UnbindCommand(char *command)
continue;
}
if (!strncmp(b, command, l))
if (!strcmp(b, command))
{
Key_SetBinding(j, "");
}
@ -852,17 +839,28 @@ M_UnbindCommand(char *command)
}
static void
M_FindKeysForCommand(char *command, int *twokeys)
M_FindKeysForCommand(char *command, int *twokeys, int scope)
{
int count;
int j;
int l;
int begin = 0, end = K_LAST;
switch (scope)
{
case KEYS_KEYBOARD_MOUSE:
end = K_JOY_FIRST_REGULAR;
break;
case KEYS_CONTROLLER:
begin = K_JOY_FIRST_REGULAR;
end = K_JOY_LAST_REGULAR + 1;
break;
case KEYS_CONTROLLER_ALT:
begin = K_JOY_FIRST_REGULAR_ALT;
}
twokeys[0] = twokeys[1] = -1;
l = strlen(command);
count = 0;
for (j = 0; j < K_LAST; j++)
for (j = begin; j < end; j++)
{
char *b;
b = keybindings[j];
@ -872,7 +870,7 @@ M_FindKeysForCommand(char *command, int *twokeys)
continue;
}
if (!strncmp(b, command, l))
if (!strcmp(b, command))
{
twokeys[count] = j;
count++;
@ -908,7 +906,7 @@ DrawKeyBindingFunc(void *self)
menuaction_s *a = (menuaction_s *)self;
float scale = SCR_GetMenuScale();
M_FindKeysForCommand(bindnames[a->generic.localdata[0]][0], keys);
M_FindKeysForCommand(bindnames[a->generic.localdata[0]][0], keys, KEYS_KEYBOARD_MOUSE);
if (keys[0] == -1)
{
@ -944,11 +942,11 @@ KeyBindingFunc(void *self)
menuaction_s *a = (menuaction_s *)self;
int keys[2];
M_FindKeysForCommand(bindnames[a->generic.localdata[0]][0], keys);
M_FindKeysForCommand(bindnames[a->generic.localdata[0]][0], keys, KEYS_KEYBOARD_MOUSE);
if (keys[1] != -1)
{
M_UnbindCommand(bindnames[a->generic.localdata[0]][0]);
M_UnbindCommand(bindnames[a->generic.localdata[0]][0], KEYS_KEYBOARD_MOUSE);
}
menukeyitem_bind = true;
@ -996,7 +994,8 @@ Keys_MenuKey(int key)
if (menukeyitem_bind)
{
if ((key != K_ESCAPE) && (key != '`'))
// Any key/button except from the game controller and escape keys
if ((key != K_ESCAPE) && (key != '`') && (key < K_JOY_FIRST_REGULAR))
{
char cmd[1024];
@ -1010,16 +1009,14 @@ Keys_MenuKey(int key)
return menu_out_sound;
}
key = Key_GetMenuKey(key);
switch (key)
{
case K_KP_ENTER:
case K_ENTER:
KeyBindingFunc(item);
return menu_in_sound;
case K_BACKSPACE: /* delete bindings */
case K_DEL: /* delete bindings */
case K_KP_DEL:
M_UnbindCommand(bindnames[item->generic.localdata[0]][0]);
M_UnbindCommand(bindnames[item->generic.localdata[0]][0], KEYS_KEYBOARD_MOUSE);
return menu_out_sound;
default:
return Default_MenuKey(&s_keys_menu, key);
@ -1059,7 +1056,7 @@ MultiplayerDrawKeyBindingFunc(void *self)
menuaction_s *a = (menuaction_s *)self;
float scale = SCR_GetMenuScale();
M_FindKeysForCommand(multiplayer_key_bindnames[a->generic.localdata[0]][0], keys);
M_FindKeysForCommand(multiplayer_key_bindnames[a->generic.localdata[0]][0], keys, KEYS_ALL);
if (keys[0] == -1)
{
@ -1095,11 +1092,11 @@ MultiplayerKeyBindingFunc(void *self)
menuaction_s *a = (menuaction_s *)self;
int keys[2];
M_FindKeysForCommand(multiplayer_key_bindnames[a->generic.localdata[0]][0], keys);
M_FindKeysForCommand(multiplayer_key_bindnames[a->generic.localdata[0]][0], keys, KEYS_ALL);
if (keys[1] != -1)
{
M_UnbindCommand(multiplayer_key_bindnames[a->generic.localdata[0]][0]);
M_UnbindCommand(multiplayer_key_bindnames[a->generic.localdata[0]][0], KEYS_ALL);
}
menukeyitem_bind = true;
@ -1147,7 +1144,8 @@ MultiplayerKeys_MenuKey(int key)
if (menukeyitem_bind)
{
if ((key != K_ESCAPE) && (key != '`'))
// Any key/button but the escape ones
if ((key != K_ESCAPE) && (key != '`') && (key != K_JOY_BACK))
{
char cmd[1024];
@ -1161,16 +1159,14 @@ MultiplayerKeys_MenuKey(int key)
return menu_out_sound;
}
key = Key_GetMenuKey(key);
switch (key)
{
case K_KP_ENTER:
case K_ENTER:
MultiplayerKeyBindingFunc(item);
return menu_in_sound;
case K_BACKSPACE: /* delete bindings */
case K_DEL: /* delete bindings */
case K_KP_DEL:
M_UnbindCommand(multiplayer_key_bindnames[item->generic.localdata[0]][0]);
M_UnbindCommand(multiplayer_key_bindnames[item->generic.localdata[0]][0], KEYS_ALL);
return menu_out_sound;
default:
return Default_MenuKey(&s_multiplayer_keys_menu, key);
@ -1184,6 +1180,332 @@ M_Menu_Multiplayer_Keys_f(void)
M_PushMenu(MultiplayerKeys_MenuDraw, MultiplayerKeys_MenuKey);
}
/*
* GAME CONTROLLER ( GAMEPAD / JOYSTICK ) BUTTONS MENU
*/
char *controller_bindnames[][2] =
{
{"+attack", "attack"},
{"+moveup", "up / jump"},
{"+movedown", "down / crouch"},
{"weapnext", "next weapon"},
{"weapprev", "previous weapon"},
{"cycleweap weapon_chaingun weapon_machinegun weapon_blaster", "long range: quickswitch 1"},
{"cycleweap weapon_supershotgun weapon_shotgun", "close range: quickswitch 2"},
{"cycleweap weapon_rocketlauncher weapon_grenadelauncher ammo_grenades", "explosives: quickswitch 3"},
{"cycleweap weapon_bfg weapon_railgun weapon_hyperblaster", "special: quickswitch 4"},
{"prefweap weapon_railgun weapon_hyperblaster weapon_chaingun weapon_supershotgun weapon_machinegun weapon_shotgun weapon_blaster", "best safe weapon"},
{"prefweap weapon_bfg weapon_railgun weapon_rocketlauncher weapon_hyperblaster weapon_grenadelauncher weapon_chaingun ammo_grenades weapon_supershotgun", "best unsafe weapon"},
{"centerview", "center view"},
{"inven", "inventory"},
{"invuse", "use item"},
{"invdrop", "drop item"},
{"invprev", "prev item"},
{"invnext", "next item"},
{"cmd help", "help computer"},
{"+joyaltselector", "alt buttons modifier"}
};
#define NUM_CONTROLLER_BINDNAMES (sizeof controller_bindnames / sizeof controller_bindnames[0])
static menuframework_s s_controller_buttons_menu;
static menuaction_s s_controller_buttons_actions[NUM_CONTROLLER_BINDNAMES];
static void
DrawControllerButtonBindingFunc(void *self)
{
int keys[2];
menuaction_s *a = (menuaction_s *)self;
float scale = SCR_GetMenuScale();
M_FindKeysForCommand(controller_bindnames[a->generic.localdata[0]][0], keys, KEYS_CONTROLLER);
if (keys[0] == -1)
{
Menu_DrawString(a->generic.x + a->generic.parent->x + RCOLUMN_OFFSET * scale,
a->generic.y + a->generic.parent->y, "???");
}
else
{
int x;
const char *name;
name = Key_KeynumToString(keys[0]);
Menu_DrawString(a->generic.x + a->generic.parent->x + RCOLUMN_OFFSET * scale,
a->generic.y + a->generic.parent->y, name);
x = strlen(name) * 8;
if (keys[1] != -1)
{
Menu_DrawString(a->generic.x + a->generic.parent->x + 24 * scale + (x * scale),
a->generic.y + a->generic.parent->y, "or");
Menu_DrawString(a->generic.x + a->generic.parent->x + 48 * scale + (x * scale),
a->generic.y + a->generic.parent->y,
Key_KeynumToString(keys[1]));
}
}
}
static void
ControllerButtonBindingFunc(void *self)
{
menuaction_s *a = (menuaction_s *)self;
int keys[2];
M_FindKeysForCommand(controller_bindnames[a->generic.localdata[0]][0], keys, KEYS_CONTROLLER);
if (keys[1] != -1)
{
M_UnbindCommand(controller_bindnames[a->generic.localdata[0]][0], KEYS_CONTROLLER);
}
menukeyitem_bind = true;
Menu_SetStatusBar(&s_controller_buttons_menu, "press a button for this action");
}
static void
ControllerButtons_MenuInit(void)
{
int i;
s_controller_buttons_menu.x = (int)(viddef.width * 0.50f);
s_controller_buttons_menu.nitems = 0;
s_controller_buttons_menu.cursordraw = KeyCursorDrawFunc;
for (i = 0; i < NUM_CONTROLLER_BINDNAMES; i++)
{
s_controller_buttons_actions[i].generic.type = MTYPE_ACTION;
s_controller_buttons_actions[i].generic.flags = QMF_GRAYED;
s_controller_buttons_actions[i].generic.x = 0;
s_controller_buttons_actions[i].generic.y = (i * 9);
s_controller_buttons_actions[i].generic.ownerdraw = DrawControllerButtonBindingFunc;
s_controller_buttons_actions[i].generic.localdata[0] = i;
s_controller_buttons_actions[i].generic.name = controller_bindnames[s_controller_buttons_actions[i].generic.localdata[0]][1];
Menu_AddItem(&s_controller_buttons_menu, (void *)&s_controller_buttons_actions[i]);
}
Menu_SetStatusBar(&s_controller_buttons_menu, "BTN_A assigns, BTN_Y clears, BTN_B exits");
Menu_Center(&s_controller_buttons_menu);
}
static void
ControllerButtons_MenuDraw(void)
{
Menu_AdjustCursor(&s_controller_buttons_menu, 1);
Menu_Draw(&s_controller_buttons_menu);
}
static const char *
ControllerButtons_MenuKey(int key)
{
menuaction_s *item = (menuaction_s *)Menu_ItemAtCursor(&s_controller_buttons_menu);
if (menukeyitem_bind)
{
// Only controller buttons allowed
if (key >= K_JOY_FIRST_REGULAR && key != K_JOY_BACK)
{
char cmd[1024];
Com_sprintf(cmd, sizeof(cmd), "bind \"%s\" \"%s\"\n",
Key_KeynumToString(key), controller_bindnames[item->generic.localdata[0]][0]);
Cbuf_InsertText(cmd);
}
Menu_SetStatusBar(&s_controller_buttons_menu, "BTN_A assigns, BTN_Y clears, BTN_B exits");
menukeyitem_bind = false;
return menu_out_sound;
}
key = Key_GetMenuKey(key);
switch (key)
{
case K_ENTER:
ControllerButtonBindingFunc(item);
return menu_in_sound;
case K_BACKSPACE:
M_UnbindCommand(controller_bindnames[item->generic.localdata[0]][0], KEYS_CONTROLLER);
return menu_out_sound;
default:
return Default_MenuKey(&s_controller_buttons_menu, key);
}
}
static void
M_Menu_ControllerButtons_f(void)
{
ControllerButtons_MenuInit();
M_PushMenu(ControllerButtons_MenuDraw, ControllerButtons_MenuKey);
}
/*
* GAME CONTROLLER ALTERNATE BUTTONS MENU
*/
char *controller_alt_bindnames[][2] =
{
{"weapnext", "next weapon"},
{"weapprev", "previous weapon"},
{"cycleweap weapon_chaingun weapon_machinegun weapon_blaster", "long range: quickswitch 1"},
{"cycleweap weapon_supershotgun weapon_shotgun", "close range: quickswitch 2"},
{"cycleweap weapon_rocketlauncher weapon_grenadelauncher ammo_grenades", "explosives: quickswitch 3"},
{"cycleweap weapon_bfg weapon_railgun weapon_hyperblaster", "special: quickswitch 4"},
{"prefweap weapon_railgun weapon_hyperblaster weapon_chaingun weapon_supershotgun weapon_machinegun weapon_shotgun weapon_blaster", "best safe weapon"},
{"prefweap weapon_bfg weapon_railgun weapon_rocketlauncher weapon_hyperblaster weapon_grenadelauncher weapon_chaingun ammo_grenades weapon_supershotgun", "best unsafe weapon"},
{"centerview", "center view"},
{"inven", "inventory"},
{"invuse", "use item"},
{"invdrop", "drop item"},
{"invprev", "prev item"},
{"invnext", "next item"},
{"use invulnerability", "use invulnerability"},
{"use rebreather", "use rebreather"},
{"use environment suit", "use environment suit"},
{"use power shield", "use power shield"},
{"use quad damage", "use quad damage"},
{"cmd help", "help computer"}
};
#define NUM_CONTROLLER_ALT_BINDNAMES (sizeof controller_alt_bindnames / sizeof controller_alt_bindnames[0])
static menuframework_s s_controller_alt_buttons_menu;
static menuaction_s s_controller_alt_buttons_actions[NUM_CONTROLLER_ALT_BINDNAMES];
static void
DrawControllerAltButtonBindingFunc(void *self)
{
int keys[2];
menuaction_s *a = (menuaction_s *)self;
float scale = SCR_GetMenuScale();
M_FindKeysForCommand(controller_alt_bindnames[a->generic.localdata[0]][0], keys, KEYS_CONTROLLER_ALT);
if (keys[0] == -1)
{
Menu_DrawString(a->generic.x + a->generic.parent->x + RCOLUMN_OFFSET * scale,
a->generic.y + a->generic.parent->y, "???");
}
else
{
int x;
const char *name;
name = Key_KeynumToString(keys[0]);
Menu_DrawString(a->generic.x + a->generic.parent->x + RCOLUMN_OFFSET * scale,
a->generic.y + a->generic.parent->y, name);
x = strlen(name) * 8;
if (keys[1] != -1)
{
Menu_DrawString(a->generic.x + a->generic.parent->x + 24 * scale + (x * scale),
a->generic.y + a->generic.parent->y, "or");
Menu_DrawString(a->generic.x + a->generic.parent->x + 48 * scale + (x * scale),
a->generic.y + a->generic.parent->y,
Key_KeynumToString(keys[1]));
}
}
}
static void
ControllerAltButtonBindingFunc(void *self)
{
menuaction_s *a = (menuaction_s *)self;
int keys[2];
M_FindKeysForCommand(controller_alt_bindnames[a->generic.localdata[0]][0], keys, KEYS_CONTROLLER_ALT);
if (keys[1] != -1)
{
M_UnbindCommand(controller_alt_bindnames[a->generic.localdata[0]][0], KEYS_CONTROLLER_ALT);
}
menukeyitem_bind = true;
Menu_SetStatusBar(&s_controller_alt_buttons_menu, "press a button for this action");
}
static void
ControllerAltButtons_MenuInit(void)
{
int i;
s_controller_alt_buttons_menu.x = (int)(viddef.width * 0.50f);
s_controller_alt_buttons_menu.nitems = 0;
s_controller_alt_buttons_menu.cursordraw = KeyCursorDrawFunc;
for (i = 0; i < NUM_CONTROLLER_ALT_BINDNAMES; i++)
{
s_controller_alt_buttons_actions[i].generic.type = MTYPE_ACTION;
s_controller_alt_buttons_actions[i].generic.flags = QMF_GRAYED;
s_controller_alt_buttons_actions[i].generic.x = 0;
s_controller_alt_buttons_actions[i].generic.y = (i * 9);
s_controller_alt_buttons_actions[i].generic.ownerdraw = DrawControllerAltButtonBindingFunc;
s_controller_alt_buttons_actions[i].generic.localdata[0] = i;
s_controller_alt_buttons_actions[i].generic.name = controller_alt_bindnames[s_controller_alt_buttons_actions[i].generic.localdata[0]][1];
Menu_AddItem(&s_controller_alt_buttons_menu, (void *)&s_controller_alt_buttons_actions[i]);
}
Menu_SetStatusBar(&s_controller_alt_buttons_menu, "BTN_A assigns, BTN_Y clears, BTN_B exits");
Menu_Center(&s_controller_alt_buttons_menu);
}
static void
ControllerAltButtons_MenuDraw(void)
{
Menu_AdjustCursor(&s_controller_alt_buttons_menu, 1);
Menu_Draw(&s_controller_alt_buttons_menu);
}
static const char *
ControllerAltButtons_MenuKey(int key)
{
menuaction_s *item = (menuaction_s *)Menu_ItemAtCursor(&s_controller_alt_buttons_menu);
if (menukeyitem_bind)
{
// Only controller buttons allowed, different from the alt buttons modifier
if (key >= K_JOY_FIRST_REGULAR && key != K_JOY_BACK && (keybindings[key] == NULL || strcmp(keybindings[key], "+joyaltselector") != 0))
{
char cmd[1024];
key = key + (K_JOY_FIRST_REGULAR_ALT - K_JOY_FIRST_REGULAR); // change input to its ALT mode
Com_sprintf(cmd, sizeof(cmd), "bind \"%s\" \"%s\"\n",
Key_KeynumToString(key), controller_alt_bindnames[item->generic.localdata[0]][0]);
Cbuf_InsertText(cmd);
}
Menu_SetStatusBar(&s_controller_alt_buttons_menu, "BTN_A assigns, BTN_Y clears, BTN_B exits");
menukeyitem_bind = false;
return menu_out_sound;
}
key = Key_GetMenuKey(key);
switch (key)
{
case K_ENTER:
ControllerAltButtonBindingFunc(item);
return menu_in_sound;
case K_BACKSPACE:
M_UnbindCommand(controller_alt_bindnames[item->generic.localdata[0]][0], KEYS_CONTROLLER_ALT);
return menu_out_sound;
default:
return Default_MenuKey(&s_controller_alt_buttons_menu, key);
}
}
static void
M_Menu_ControllerAltButtons_f(void)
{
ControllerAltButtons_MenuInit();
M_PushMenu(ControllerAltButtons_MenuDraw, ControllerAltButtons_MenuKey);
}
/*
* JOY MENU
*/
@ -1194,6 +1516,20 @@ static menuslider_s s_joy_forwardsensitivity_slider;
static menuslider_s s_joy_sidesensitivity_slider;
static menuslider_s s_joy_upsensitivity_slider;
static menuslider_s s_joy_haptic_slider;
static menuaction_s s_joy_customize_buttons_action;
static menuaction_s s_joy_customize_alt_buttons_action;
static void
CustomizeControllerButtonsFunc(void *unused)
{
M_Menu_ControllerButtons_f();
}
static void
CustomizeControllerAltButtonsFunc(void *unused)
{
M_Menu_ControllerAltButtons_f();
}
static void
HapticMagnitudeFunc(void *unused)
@ -1333,6 +1669,24 @@ Joy_MenuInit(void)
Menu_AddItem(&s_joy_menu, (void *)&s_joy_haptic_slider);
}
y += 10;
s_joy_customize_buttons_action.generic.type = MTYPE_ACTION;
s_joy_customize_buttons_action.generic.x = 0;
s_joy_customize_buttons_action.generic.y = y;
y += 10;
s_joy_customize_buttons_action.generic.name = "customize buttons";
s_joy_customize_buttons_action.generic.callback = CustomizeControllerButtonsFunc;
Menu_AddItem(&s_joy_menu, (void *)&s_joy_customize_buttons_action);
s_joy_customize_alt_buttons_action.generic.type = MTYPE_ACTION;
s_joy_customize_alt_buttons_action.generic.x = 0;
s_joy_customize_alt_buttons_action.generic.y = y;
y += 10;
s_joy_customize_alt_buttons_action.generic.name = "customize alt buttons";
s_joy_customize_alt_buttons_action.generic.callback = CustomizeControllerAltButtonsFunc;
Menu_AddItem(&s_joy_menu, (void *)&s_joy_customize_alt_buttons_action);
Menu_Center(&s_joy_menu);
}
@ -1702,7 +2056,7 @@ Options_MenuInit(void)
s_options_customize_joy_action.generic.type = MTYPE_ACTION;
s_options_customize_joy_action.generic.x = 0;
s_options_customize_joy_action.generic.y = 130;
s_options_customize_joy_action.generic.name = "customize joystick";
s_options_customize_joy_action.generic.name = "customize gamepad";
s_options_customize_joy_action.generic.callback = CustomizeJoyFunc;
s_options_customize_options_action.generic.type = MTYPE_ACTION;
@ -2892,8 +3246,6 @@ LoadGame_MenuKey(int key)
return menu_move_sound;
case K_BACKSPACE:
case K_DEL:
case K_KP_DEL:
if ((item = Menu_ItemAtCursor(m)) != NULL)
{
if (item->type == MTYPE_ACTION)
@ -3064,8 +3416,6 @@ SaveGame_MenuKey(int key)
return menu_move_sound;
case K_BACKSPACE:
case K_DEL:
case K_KP_DEL:
if ((item = Menu_ItemAtCursor(m)) != NULL)
{
if (item->type == MTYPE_ACTION)
@ -5078,6 +5428,8 @@ M_Init(void)
Cmd_AddCommand("menu_options", M_Menu_Options_f);
Cmd_AddCommand("menu_keys", M_Menu_Keys_f);
Cmd_AddCommand("menu_joy", M_Menu_Joy_f);
Cmd_AddCommand("menu_buttons", M_Menu_ControllerButtons_f);
Cmd_AddCommand("menu_altbuttons", M_Menu_ControllerAltButtons_f);
Cmd_AddCommand("menu_quit", M_Menu_Quit_f);
/* initialize the server address book cvars (adr0, adr1, ...)