diff --git a/quakespasm/Misc/qs_pak/default.cfg b/quakespasm/Misc/qs_pak/default.cfg index 27186fd0..25a4b053 100644 --- a/quakespasm/Misc/qs_pak/default.cfg +++ b/quakespasm/Misc/qs_pak/default.cfg @@ -93,6 +93,14 @@ bind MOUSE1 +attack bind MOUSE2 +jump //bind MOUSE3 +mlook +// +// game controller +// +bind LSHOULDER "impulse 12" +bind RSHOULDER "impulse 10" +bind LTRIGGER +jump +bind RTRIGGER +attack + // // default cvars // diff --git a/quakespasm/Quake/gl_draw.c b/quakespasm/Quake/gl_draw.c index ff6d213c..b8cbe491 100644 --- a/quakespasm/Quake/gl_draw.c +++ b/quakespasm/Quake/gl_draw.c @@ -701,10 +701,10 @@ void GL_SetCanvas (canvastype newcanvas) glViewport (glx, gly, glwidth, glheight); break; case CANVAS_MENU: - s = q_min((float)glwidth / 320.0, (float)glheight / 200.0); + s = q_min((float)glwidth / 640.0, (float)glheight / 200.0); // ericw -- doubled width to 640 to accommodate long keybindings s = CLAMP (1.0, scr_menuscale.value, s); - glOrtho (0, 320, 200, 0, -99999, 99999); - glViewport (glx + (glwidth - 320*s) / 2, gly + (glheight - 200*s) / 2, 320*s, 200*s); + glOrtho (0, 640, 200, 0, -99999, 99999); + glViewport (glx + (glwidth - 320*s) / 2, gly + (glheight - 200*s) / 2, 640*s, 200*s); break; case CANVAS_SBAR: s = CLAMP (1.0, scr_sbarscale.value, (float)glwidth / 320.0); diff --git a/quakespasm/Quake/gl_screen.c b/quakespasm/Quake/gl_screen.c index 1c3c8065..0d560471 100644 --- a/quakespasm/Quake/gl_screen.c +++ b/quakespasm/Quake/gl_screen.c @@ -924,6 +924,8 @@ int SCR_ModalMessage (const char *text, float timeout) //johnfitz -- timeout } while (lastchar != 'y' && lastchar != 'Y' && lastchar != 'n' && lastchar != 'N' && lastkey != K_ESCAPE && + lastkey != K_ABUTTON && + lastkey != K_BBUTTON && time2 <= time1); Key_EndInputGrab (); @@ -934,7 +936,7 @@ int SCR_ModalMessage (const char *text, float timeout) //johnfitz -- timeout return false; //johnfitz - return (lastchar == 'y' || lastchar == 'Y'); + return (lastchar == 'y' || lastchar == 'Y' || lastkey == K_ABUTTON); } diff --git a/quakespasm/Quake/gl_vidsdl.c b/quakespasm/Quake/gl_vidsdl.c index 32c8e6ef..e9e0c3ac 100644 --- a/quakespasm/Quake/gl_vidsdl.c +++ b/quakespasm/Quake/gl_vidsdl.c @@ -1951,6 +1951,7 @@ static void VID_MenuKey (int key) switch (key) { case K_ESCAPE: + case K_BBUTTON: VID_SyncCvars (); //sync cvars before leaving menu. FIXME: there are other ways to leave menu S_LocalSound ("misc/menu1.wav"); M_Menu_Options_f (); @@ -2014,6 +2015,7 @@ static void VID_MenuKey (int key) case K_ENTER: case K_KP_ENTER: + case K_ABUTTON: m_entersound = true; switch (video_options_cursor) { diff --git a/quakespasm/Quake/in_sdl.c b/quakespasm/Quake/in_sdl.c index 752f7a53..cae320e9 100644 --- a/quakespasm/Quake/in_sdl.c +++ b/quakespasm/Quake/in_sdl.c @@ -48,6 +48,21 @@ static cvar_t in_debugkeys = {"in_debugkeys", "0", CVAR_NONE}; #include #endif +// SDL2 Game Controller cvars +cvar_t joy_deadzone = { "joy_deadzone", "0.2", CVAR_NONE }; +cvar_t joy_deadzone_trigger = { "joy_deadzone_trigger", "0.001", CVAR_NONE }; +cvar_t joy_sensitivity_yaw = { "joy_sensitivity_yaw", "300", CVAR_NONE }; +cvar_t joy_sensitivity_pitch = { "joy_sensitivity_pitch", "150", CVAR_NONE }; +cvar_t joy_invert = { "joy_invert", "0", CVAR_NONE }; +cvar_t joy_exponent = { "joy_exponent", "3", CVAR_NONE }; +cvar_t joy_swapmovelook = { "joy_swapmovelook", "0", CVAR_NONE }; +cvar_t joy_enable = { "joy_enable", "1", CVAR_NONE }; + +#if defined(USE_SDL2) +static SDL_JoystickID joy_active_instaceid = -1; +static SDL_GameController *joy_active_controller = NULL; +#endif + static qboolean no_mouse = false; static int buttonremap[] = @@ -257,6 +272,63 @@ void IN_Deactivate (qboolean free_cursor) IN_BeginIgnoringMouseEvents(); } +void IN_StartupJoystick (void) +{ +#if defined(USE_SDL2) + int i; + int nummappings; + char controllerdb[MAX_OSPATH]; + SDL_GameController *gamecontroller; + + if (COM_CheckParm("-nojoy")) + return; + + // Load additional SDL2 controller definitions from gamecontrollerdb.txt + q_snprintf (controllerdb, sizeof(controllerdb), "%s/gamecontrollerdb.txt", com_basedir); + nummappings = SDL_GameControllerAddMappingsFromFile(controllerdb); + if (nummappings) + Con_Printf("%d mappings loaded from gamecontrollerdb.txt\n", nummappings); + + // Also try host_parms->userdir + if (host_parms->userdir != host_parms->basedir) + { + q_snprintf (controllerdb, sizeof(controllerdb), "%s/gamecontrollerdb.txt", host_parms->userdir); + nummappings = SDL_GameControllerAddMappingsFromFile(controllerdb); + if (nummappings) + Con_Printf("%d mappings loaded from gamecontrollerdb.txt\n", nummappings); + } + + if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == -1 ) + { + Con_Printf("WARNING: Could not initialize SDL Game Controller\n"); + return; + } + + for (i = 0; i < SDL_NumJoysticks(); i++) + { + if ( SDL_IsGameController(i) ) + { + gamecontroller = SDL_GameControllerOpen(i); + if (gamecontroller) + { + Con_Printf("detected controller: %s\n", SDL_GameControllerNameForIndex(i)); + + joy_active_instaceid = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontroller)); + joy_active_controller = gamecontroller; + break; + } + } + } +#endif +} + +void IN_ShutdownJoystick (void) +{ +#if defined(USE_SDL2) + SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER); +#endif +} + void IN_Init (void) { textmode = Key_TextEntry(); @@ -282,31 +354,328 @@ void IN_Init (void) Cvar_RegisterVariable(&in_disablemacosxmouseaccel); #endif Cvar_RegisterVariable(&in_debugkeys); + Cvar_RegisterVariable(&joy_sensitivity_yaw); + Cvar_RegisterVariable(&joy_sensitivity_pitch); + Cvar_RegisterVariable(&joy_deadzone); + Cvar_RegisterVariable(&joy_deadzone_trigger); + Cvar_RegisterVariable(&joy_invert); + Cvar_RegisterVariable(&joy_exponent); + Cvar_RegisterVariable(&joy_swapmovelook); + Cvar_RegisterVariable(&joy_enable); IN_Activate(); + IN_StartupJoystick(); } void IN_Shutdown (void) { IN_Deactivate(true); -} - -void IN_Commands (void) -{ -/* TODO: implement this for joystick support */ + IN_ShutdownJoystick(); } extern cvar_t cl_maxpitch; /* johnfitz -- variable pitch clamping */ extern cvar_t cl_minpitch; /* johnfitz -- variable pitch clamping */ -void IN_MouseMove(int dx, int dy) +void IN_MouseMotion(int dx, int dy) { total_dx += dx; total_dy += dy; } -void IN_Move (usercmd_t *cmd) +#if defined(USE_SDL2) +typedef struct joyaxis_s +{ + float x; + float y; +} joyaxis_t; + +typedef struct joy_buttonstate_s +{ + qboolean buttondown[SDL_CONTROLLER_BUTTON_MAX]; +} joybuttonstate_t; + +typedef struct axisstate_s +{ + float axisvalue[SDL_CONTROLLER_AXIS_MAX]; // normalized to +-1 +} joyaxisstate_t; + +static joybuttonstate_t joy_buttonstate; +static joyaxisstate_t joy_axisstate; + +static double joy_buttontimer[SDL_CONTROLLER_BUTTON_MAX]; +static double joy_emulatedkeytimer[10]; + +/* +================ +IN_ApplyEasing + +assumes axis values are in [-1, 1]. Raises the axis values to the given exponent, keeping signs. +================ +*/ +static joyaxis_t IN_ApplyEasing(joyaxis_t axis, float exponent) +{ + joyaxis_t result = {0}; + float magnitude, eased_magnitude; + + magnitude = sqrtf( (axis.x * axis.x) + (axis.y * axis.y) ); + + if (magnitude > 1) + magnitude = 1; + + if (magnitude == 0) + return result; + + eased_magnitude = powf(magnitude, exponent); + + result.x = axis.x * (eased_magnitude / magnitude); + result.y = axis.y * (eased_magnitude / magnitude); + return result; +} + +/* +================ +IN_ApplyMoveEasing + +clamps coordinates to a square with coordinates +/- sqrt(2)/2, then scales them to +/- 1. +This wastes a bit of stick range, but gives the diagonals coordinates of (+/-1,+/-1), +so holding the stick on a diagonal gives the same speed boost as holding the forward and strafe keyboard keys. +================ +*/ +static joyaxis_t IN_ApplyMoveEasing(joyaxis_t axis) +{ + joyaxis_t result = {0}; + const float v = sqrtf(2.0f) / 2.0f; + + result.x = q_max(-v, q_min(v, axis.x)); + result.y = q_max(-v, q_min(v, axis.y)); + + result.x /= v; + result.y /= v; + + return result; +} + + +/* +================ +IN_ApplyDeadzone + +from https://github.com/jeremiah-sypult/Quakespasm-Rift +and adapted from http://www.third-helix.com/2013/04/12/doing-thumbstick-dead-zones-right.html +================ +*/ +static joyaxis_t IN_ApplyDeadzone(joyaxis_t axis, float deadzone) +{ + joyaxis_t result = {0}; + float magnitude = sqrtf( (axis.x * axis.x) + (axis.y * axis.y) ); + + if ( magnitude < deadzone ) { + result.x = result.y = 0.0f; + } else { + joyaxis_t normalized; + float gradient; + + if ( magnitude > 1.0f ) { + magnitude = 1.0f; + } + + normalized.x = axis.x / magnitude; + normalized.y = axis.y / magnitude; + gradient = ( (magnitude - deadzone) / (1.0f - deadzone) ); + result.x = normalized.x * gradient; + result.y = normalized.y * gradient; + } + + return result; +} + +/* +================ +IN_KeyForControllerButton +================ +*/ +static int IN_KeyForControllerButton(SDL_GameControllerButton button) +{ + switch (button) + { + case SDL_CONTROLLER_BUTTON_A: return K_ABUTTON; + case SDL_CONTROLLER_BUTTON_B: return K_BBUTTON; + case SDL_CONTROLLER_BUTTON_X: return K_XBUTTON; + case SDL_CONTROLLER_BUTTON_Y: return K_YBUTTON; + case SDL_CONTROLLER_BUTTON_BACK: return K_TAB; + case SDL_CONTROLLER_BUTTON_START: return K_ESCAPE; + case SDL_CONTROLLER_BUTTON_LEFTSTICK: return K_LTHUMB; + case SDL_CONTROLLER_BUTTON_RIGHTSTICK: return K_RTHUMB; + case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: return K_LSHOULDER; + case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: return K_RSHOULDER; + case SDL_CONTROLLER_BUTTON_DPAD_UP: return K_UPARROW; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: return K_DOWNARROW; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: return K_LEFTARROW; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: return K_RIGHTARROW; + default: return 0; + } +} + +/* +================ +IN_JoyKeyEvent + +Sends a Key_Event if a unpressed -> pressed or pressed -> unpressed transition occurred, +and generates key repeats if the button is held down. + +Adapted from DarkPlaces by lordhavoc +================ +*/ +static void IN_JoyKeyEvent(qboolean wasdown, qboolean isdown, int key, double *timer) +{ + // we can't use `realtime` for key repeats because it is not monotomic + const double currenttime = Sys_DoubleTime(); + + if (wasdown) + { + if (isdown) + { + if (currenttime >= *timer) + { + *timer = currenttime + 0.1; + Key_Event(key, true); + } + } + else + { + *timer = 0; + Key_Event(key, false); + } + } + else + { + if (isdown) + { + *timer = currenttime + 0.5; + Key_Event(key, true); + } + } +} +#endif + +/* +================ +IN_Commands + +Emit key events for game controller buttons, including emulated buttons for analog sticks/triggers +================ +*/ +void IN_Commands (void) +{ +#if defined(USE_SDL2) + joyaxisstate_t newaxisstate; + int i; + const float stickthreshold = 0.9; + const float triggerthreshold = joy_deadzone_trigger.value; + + if (!joy_enable.value) + return; + + if (!joy_active_controller) + return; + + // emit key events for controller buttons + for (i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++) + { + qboolean newstate = SDL_GameControllerGetButton(joy_active_controller, i); + qboolean oldstate = joy_buttonstate.buttondown[i]; + + joy_buttonstate.buttondown[i] = newstate; + + // NOTE: This can cause a reentrant call of IN_Commands, via SCR_ModalMessage when confirming a new game. + IN_JoyKeyEvent(oldstate, newstate, IN_KeyForControllerButton(i), &joy_buttontimer[i]); + } + + for (i = 0; i < SDL_CONTROLLER_AXIS_MAX; i++) + { + newaxisstate.axisvalue[i] = SDL_GameControllerGetAxis(joy_active_controller, i) / 32768.0f; + } + + // emit emulated arrow keys so the analog sticks can be used in the menu + if (key_dest != key_game) + { + IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTX] < -stickthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTX] < -stickthreshold, K_LEFTARROW, &joy_emulatedkeytimer[0]); + IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTX] > stickthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTX] > stickthreshold, K_RIGHTARROW, &joy_emulatedkeytimer[1]); + IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTY] < -stickthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTY] < -stickthreshold, K_UPARROW, &joy_emulatedkeytimer[2]); + IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTY] > stickthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTY] > stickthreshold, K_DOWNARROW, &joy_emulatedkeytimer[3]); + IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTX] < -stickthreshold,newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTX] < -stickthreshold, K_LEFTARROW, &joy_emulatedkeytimer[4]); + IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTX] > stickthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTX] > stickthreshold, K_RIGHTARROW, &joy_emulatedkeytimer[5]); + IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTY] < -stickthreshold,newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTY] < -stickthreshold, K_UPARROW, &joy_emulatedkeytimer[6]); + IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTY] > stickthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTY] > stickthreshold, K_DOWNARROW, &joy_emulatedkeytimer[7]); + } + + // emit emulated keys for the analog triggers + IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_TRIGGERLEFT] > triggerthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_TRIGGERLEFT] > triggerthreshold, K_LTRIGGER, &joy_emulatedkeytimer[8]); + IN_JoyKeyEvent(joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_TRIGGERRIGHT] > triggerthreshold, newaxisstate.axisvalue[SDL_CONTROLLER_AXIS_TRIGGERRIGHT] > triggerthreshold, K_RTRIGGER, &joy_emulatedkeytimer[9]); + + joy_axisstate = newaxisstate; +#endif +} + +/* +================ +IN_JoyMove +================ +*/ +void IN_JoyMove (usercmd_t *cmd) +{ +#if defined(USE_SDL2) + float speed; + joyaxis_t moveAxis, lookAxis; + + if (!joy_enable.value) + return; + + if (!joy_active_controller) + return; + + moveAxis.x = joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTX]; + moveAxis.y = joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_LEFTY]; + lookAxis.x = joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTX]; + lookAxis.y = joy_axisstate.axisvalue[SDL_CONTROLLER_AXIS_RIGHTY]; + + if (joy_swapmovelook.value) + { + joyaxis_t temp = moveAxis; + moveAxis = lookAxis; + lookAxis = temp; + } + + moveAxis = IN_ApplyDeadzone(moveAxis, joy_deadzone.value); + lookAxis = IN_ApplyDeadzone(lookAxis, joy_deadzone.value); + + moveAxis = IN_ApplyMoveEasing(moveAxis); + lookAxis = IN_ApplyEasing(lookAxis, joy_exponent.value); + + if (in_speed.state & 1) + speed = cl_movespeedkey.value; + else + speed = 1; + + cmd->sidemove += (cl_sidespeed.value * speed * moveAxis.x); + cmd->forwardmove -= (cl_forwardspeed.value * speed * moveAxis.y); + + cl.viewangles[YAW] -= lookAxis.x * joy_sensitivity_yaw.value * host_frametime; + cl.viewangles[PITCH] += lookAxis.y * joy_sensitivity_pitch.value * (joy_invert.value ? -1.0 : 1.0) * host_frametime; + + if (lookAxis.x != 0 || lookAxis.y != 0) + V_StopPitchDrift(); + + /* johnfitz -- variable pitch clamping */ + if (cl.viewangles[PITCH] > cl_maxpitch.value) + cl.viewangles[PITCH] = cl_maxpitch.value; + if (cl.viewangles[PITCH] < cl_minpitch.value) + cl.viewangles[PITCH] = cl_minpitch.value; +#endif +} + +void IN_MouseMove(usercmd_t *cmd) { int dmx, dmy; @@ -345,6 +714,12 @@ void IN_Move (usercmd_t *cmd) } } +void IN_Move(usercmd_t *cmd) +{ + IN_JoyMove(cmd); + IN_MouseMove(cmd); +} + void IN_ClearStates (void) { } @@ -682,9 +1057,41 @@ void IN_SendKeyEvents (void) #endif case SDL_MOUSEMOTION: - IN_MouseMove(event.motion.xrel, event.motion.yrel); + IN_MouseMotion(event.motion.xrel, event.motion.yrel); break; +#if defined(USE_SDL2) + case SDL_CONTROLLERDEVICEADDED: + if (joy_active_instaceid == -1) + { + joy_active_controller = SDL_GameControllerOpen(event.cdevice.which); + if (joy_active_controller == NULL) + Con_DPrintf("Couldn't open game controller\n"); + else + { + SDL_Joystick *joy; + joy = SDL_GameControllerGetJoystick(joy_active_controller); + joy_active_instaceid = SDL_JoystickInstanceID(joy); + } + } + else + Con_DPrintf("Ignoring SDL_CONTROLLERDEVICEADDED\n"); + break; + case SDL_CONTROLLERDEVICEREMOVED: + if (joy_active_instaceid != -1 && event.cdevice.which == joy_active_instaceid) + { + SDL_GameControllerClose(joy_active_controller); + joy_active_controller = NULL; + joy_active_instaceid = -1; + } + else + Con_DPrintf("Ignoring SDL_CONTROLLERDEVICEREMOVED\n"); + break; + case SDL_CONTROLLERDEVICEREMAPPED: + Con_DPrintf("Ignoring SDL_CONTROLLERDEVICEREMAPPED\n"); + break; +#endif + case SDL_QUIT: CL_Disconnect (); Sys_Quit (); diff --git a/quakespasm/Quake/input.h b/quakespasm/Quake/input.h index ed7877fa..fe551fb0 100644 --- a/quakespasm/Quake/input.h +++ b/quakespasm/Quake/input.h @@ -32,7 +32,7 @@ void IN_Commands (void); // oportunity for devices to stick commands on the script buffer // mouse moved by dx and dy pixels -void IN_MouseMove(int dx, int dy); +void IN_MouseMotion(int dx, int dy); void IN_SendKeyEvents (void); diff --git a/quakespasm/Quake/keys.c b/quakespasm/Quake/keys.c index 9773976b..69e2c20b 100644 --- a/quakespasm/Quake/keys.c +++ b/quakespasm/Quake/keys.c @@ -40,8 +40,6 @@ int history_line = 0; keydest_t key_dest; -#define MAX_KEYS 256 - char *keybindings[MAX_KEYS]; qboolean consolekeys[MAX_KEYS]; // if true, can't be rebound while in console qboolean menubound[MAX_KEYS]; // if true, can't be rebound while in menu @@ -163,6 +161,17 @@ keyname_t keynames[] = {"BACKQUOTE", '`'}, // because a raw backquote may toggle the console {"TILDE", '~'}, // because a raw tilde may toggle the console + {"LTHUMB", K_LTHUMB}, + {"RTHUMB", K_RTHUMB}, + {"LSHOULDER", K_LSHOULDER}, + {"RSHOULDER", K_RSHOULDER}, + {"ABUTTON", K_ABUTTON}, + {"BBUTTON", K_BBUTTON}, + {"XBUTTON", K_XBUTTON}, + {"YBUTTON", K_YBUTTON}, + {"LTRIGGER", K_LTRIGGER}, + {"RTRIGGER", K_RTRIGGER}, + {NULL, 0} }; diff --git a/quakespasm/Quake/keys.h b/quakespasm/Quake/keys.h index 422fd4b5..02cc41fd 100644 --- a/quakespasm/Quake/keys.h +++ b/quakespasm/Quake/keys.h @@ -142,13 +142,26 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define K_MOUSE4 241 #define K_MOUSE5 242 +// SDL2 game controller keys +#define K_LTHUMB 243 +#define K_RTHUMB 244 +#define K_LSHOULDER 245 +#define K_RSHOULDER 246 +#define K_ABUTTON 247 +#define K_BBUTTON 248 +#define K_XBUTTON 249 +#define K_YBUTTON 250 +#define K_LTRIGGER 251 +#define K_RTRIGGER 252 + +#define MAX_KEYS 253 #define MAXCMDLINE 256 typedef enum {key_game, key_console, key_message, key_menu} keydest_t; extern keydest_t key_dest; -extern char *keybindings[256]; +extern char *keybindings[MAX_KEYS]; extern char key_lines[32][MAXCMDLINE]; extern int edit_line; diff --git a/quakespasm/Quake/menu.c b/quakespasm/Quake/menu.c index 21aa4b90..5a8ea155 100644 --- a/quakespasm/Quake/menu.c +++ b/quakespasm/Quake/menu.c @@ -276,6 +276,7 @@ void M_Main_Key (int key) switch (key) { case K_ESCAPE: + case K_BBUTTON: IN_Activate(); key_dest = key_game; m_state = m_none; @@ -300,6 +301,7 @@ void M_Main_Key (int key) case K_ENTER: case K_KP_ENTER: + case K_ABUTTON: m_entersound = true; switch (m_main_cursor) @@ -364,6 +366,7 @@ void M_SinglePlayer_Key (int key) switch (key) { case K_ESCAPE: + case K_BBUTTON: M_Menu_Main_f (); break; @@ -381,6 +384,7 @@ void M_SinglePlayer_Key (int key) case K_ENTER: case K_KP_ENTER: + case K_ABUTTON: m_entersound = true; switch (m_singleplayer_cursor) @@ -514,11 +518,13 @@ void M_Load_Key (int k) switch (k) { case K_ESCAPE: + case K_BBUTTON: M_Menu_SinglePlayer_f (); break; case K_ENTER: case K_KP_ENTER: + case K_ABUTTON: S_LocalSound ("misc/menu2.wav"); if (!loadable[load_cursor]) return; @@ -558,11 +564,13 @@ void M_Save_Key (int k) switch (k) { case K_ESCAPE: + case K_BBUTTON: M_Menu_SinglePlayer_f (); break; case K_ENTER: case K_KP_ENTER: + case K_ABUTTON: m_state = m_none; IN_Activate(); key_dest = key_game; @@ -628,6 +636,7 @@ void M_MultiPlayer_Key (int key) switch (key) { case K_ESCAPE: + case K_BBUTTON: M_Menu_Main_f (); break; @@ -645,6 +654,7 @@ void M_MultiPlayer_Key (int key) case K_ENTER: case K_KP_ENTER: + case K_ABUTTON: m_entersound = true; switch (m_multiplayer_cursor) { @@ -735,6 +745,7 @@ void M_Setup_Key (int k) switch (k) { case K_ESCAPE: + case K_BBUTTON: M_Menu_MultiPlayer_f (); break; @@ -774,6 +785,7 @@ forward: case K_ENTER: case K_KP_ENTER: + case K_ABUTTON: if (setup_cursor == 0 || setup_cursor == 1) return; @@ -926,6 +938,7 @@ again: switch (k) { case K_ESCAPE: + case K_BBUTTON: M_Menu_MultiPlayer_f (); break; @@ -943,6 +956,7 @@ again: case K_ENTER: case K_KP_ENTER: + case K_ABUTTON: m_entersound = true; M_Menu_LanConfig_f (); break; @@ -1221,11 +1235,13 @@ void M_Options_Key (int k) switch (k) { case K_ESCAPE: + case K_BBUTTON: M_Menu_Main_f (); break; case K_ENTER: case K_KP_ENTER: + case K_ABUTTON: m_entersound = true; switch (options_cursor) { @@ -1325,27 +1341,27 @@ void M_Menu_Keys_f (void) } -void M_FindKeysForCommand (const char *command, int *twokeys) +void M_FindKeysForCommand (const char *command, int *threekeys) { int count; int j; int l; char *b; - twokeys[0] = twokeys[1] = -1; + threekeys[0] = threekeys[1] = threekeys[2] = -1; l = strlen(command); count = 0; - for (j = 0; j < 256; j++) + for (j = 0; j < MAX_KEYS; j++) { b = keybindings[j]; if (!b) continue; if (!strncmp (b, command, l) ) { - twokeys[count] = j; + threekeys[count] = j; count++; - if (count == 2) + if (count == 3) break; } } @@ -1359,7 +1375,7 @@ void M_UnbindCommand (const char *command) l = strlen(command); - for (j = 0; j < 256; j++) + for (j = 0; j < MAX_KEYS; j++) { b = keybindings[j]; if (!b) @@ -1374,7 +1390,7 @@ extern qpic_t *pic_up, *pic_down; void M_Keys_Draw (void) { int i, x, y; - int keys[2]; + int keys[3]; const char *name; qpic_t *p; @@ -1406,8 +1422,15 @@ void M_Keys_Draw (void) x = strlen(name) * 8; if (keys[1] != -1) { + name = Key_KeynumToString (keys[1]); M_Print (140 + x + 8, y, "or"); - M_Print (140 + x + 32, y, Key_KeynumToString (keys[1])); + M_Print (140 + x + 32, y, name); + x = x + 32 + strlen(name) * 8; + if (keys[2] != -1) + { + M_Print (140 + x + 8, y, "or"); + M_Print (140 + x + 32, y, Key_KeynumToString (keys[2])); + } } } } @@ -1422,7 +1445,7 @@ void M_Keys_Draw (void) void M_Keys_Key (int k) { char cmd[80]; - int keys[2]; + int keys[3]; if (bind_grab) { // defining a key @@ -1441,6 +1464,7 @@ void M_Keys_Key (int k) switch (k) { case K_ESCAPE: + case K_BBUTTON: M_Menu_Options_f (); break; @@ -1462,9 +1486,10 @@ void M_Keys_Key (int k) case K_ENTER: // go into bind mode case K_KP_ENTER: + case K_ABUTTON: M_FindKeysForCommand (bindnames[keys_cursor][0], keys); S_LocalSound ("misc/menu2.wav"); - if (keys[1] != -1) + if (keys[2] != -1) M_UnbindCommand (bindnames[keys_cursor][0]); bind_grab = true; IN_Activate(); // activate to allow mouse key binding @@ -1527,6 +1552,7 @@ void M_Help_Key (int key) switch (key) { case K_ESCAPE: + case K_BBUTTON: M_Menu_Main_f (); break; @@ -1756,6 +1782,7 @@ void M_LanConfig_Key (int key) switch (key) { case K_ESCAPE: + case K_BBUTTON: M_Menu_Net_f (); break; @@ -1775,6 +1802,7 @@ void M_LanConfig_Key (int key) case K_ENTER: case K_KP_ENTER: + case K_ABUTTON: if (lanConfig_cursor == 0) break; @@ -2266,6 +2294,7 @@ void M_GameOptions_Key (int key) switch (key) { case K_ESCAPE: + case K_BBUTTON: M_Menu_Net_f (); break; @@ -2299,6 +2328,7 @@ void M_GameOptions_Key (int key) case K_ENTER: case K_KP_ENTER: + case K_ABUTTON: S_LocalSound ("misc/menu2.wav"); if (gameoptions_cursor == 0) { @@ -2430,6 +2460,7 @@ void M_ServerList_Key (int k) switch (k) { case K_ESCAPE: + case K_BBUTTON: M_Menu_LanConfig_f (); break; @@ -2455,6 +2486,7 @@ void M_ServerList_Key (int k) case K_ENTER: case K_KP_ENTER: + case K_ABUTTON: S_LocalSound ("misc/menu2.wav"); m_return_state = m_state; m_return_onerror = true; diff --git a/quakespasm/Quake/quakespasm.pak b/quakespasm/Quake/quakespasm.pak index c0c7b093..52a5b103 100644 Binary files a/quakespasm/Quake/quakespasm.pak and b/quakespasm/Quake/quakespasm.pak differ diff --git a/quakespasm/Quake/sys_sdl_unix.c b/quakespasm/Quake/sys_sdl_unix.c index b86858f6..32f6d816 100644 --- a/quakespasm/Quake/sys_sdl_unix.c +++ b/quakespasm/Quake/sys_sdl_unix.c @@ -469,6 +469,7 @@ void Sys_Sleep (unsigned long msecs) void Sys_SendKeyEvents (void) { + IN_Commands(); //ericw -- allow joysticks to add keys so they can be used to confirm SCR_ModalMessage IN_SendKeyEvents(); } diff --git a/quakespasm/Quake/sys_sdl_win.c b/quakespasm/Quake/sys_sdl_win.c index 89217942..431f38d7 100644 --- a/quakespasm/Quake/sys_sdl_win.c +++ b/quakespasm/Quake/sys_sdl_win.c @@ -436,6 +436,7 @@ void Sys_Sleep (unsigned long msecs) void Sys_SendKeyEvents (void) { + IN_Commands(); //ericw -- allow joysticks to add keys so they can be used to confirm SCR_ModalMessage IN_SendKeyEvents(); }