From bc6cab3ab05b5cec8755ac987607a1da295e4cd3 Mon Sep 17 00:00:00 2001 From: Wintermute0110 Date: Fri, 21 Nov 2014 02:47:21 +0900 Subject: [PATCH] * SDL joystick/gamepad support implemented and working. * Tested in Linux with both XBox360 wireless and Logitech F710 gamepads. Should work with any XBox gamepad clone wired/wireless. * Works well using SDL 1.2 or SDL 2.0 * SDL scan values are currently hard-coded. Note sure how to implement remapping at the moment (config file, GUI, ...). --- neo/framework/PlayerProfile.cpp | 6 +- neo/sys/sdl/sdl_events.cpp | 381 ++++++++++++++++++++++++++++++-- 2 files changed, 365 insertions(+), 22 deletions(-) diff --git a/neo/framework/PlayerProfile.cpp b/neo/framework/PlayerProfile.cpp index 953c8149..8fe75c2c 100644 --- a/neo/framework/PlayerProfile.cpp +++ b/neo/framework/PlayerProfile.cpp @@ -436,11 +436,13 @@ void idPlayerProfile::ExecConfig( bool save, bool forceDefault ) if( leftyFlip ) { - cmdSystem->AppendCommandText( "exec joy_lefty.cfg" ); + cmdSystem->AppendCommandText( "exec joy_lefty.cfg\n" ); + cmdSystem->AppendCommandText( "exec joy_360_0.cfg\n" ); } else { - cmdSystem->AppendCommandText( "exec joy_righty.cfg" ); + cmdSystem->AppendCommandText( "exec joy_righty.cfg\n" ); + cmdSystem->AppendCommandText( "exec joy_360_0.cfg\n" ); } cmdSystem->ExecuteCommandBuffer(); diff --git a/neo/sys/sdl/sdl_events.cpp b/neo/sys/sdl/sdl_events.cpp index 84315845..028ddc5e 100644 --- a/neo/sys/sdl/sdl_events.cpp +++ b/neo/sys/sdl/sdl_events.cpp @@ -116,6 +116,25 @@ struct mouse_poll_t static idList kbd_polls; static idList mouse_polls; +struct joystick_poll_t +{ + int action; + int value; + + joystick_poll_t() + { + } + + joystick_poll_t( int a, int v ) + { + action = a; + value = v; + } +}; +static idList joystick_polls; +SDL_Joystick *joy = NULL; +int SDL_joystick_has_hat = 0; + // RB begin static int SDL_KeyToDoom3Key( SDL_Keycode key, bool& isChar ) { @@ -551,6 +570,8 @@ Sys_InitInput */ void Sys_InitInput() { + int numJoysticks, i; + kbd_polls.SetGranularity( 64 ); mouse_polls.SetGranularity( 64 ); @@ -558,8 +579,54 @@ void Sys_InitInput() SDL_EnableUNICODE( 1 ); SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL ); #endif - in_keyboard.SetModified(); + + // WM0110: Initialise SDL Joystick + common->Printf( "Sys_InitInput: Joystick subsystem init\n" ); + if( SDL_Init(SDL_INIT_JOYSTICK) ) { + common->Printf( "Sys_InitInput: Joystic Init ERROR!\n"); + } + + numJoysticks = SDL_NumJoysticks(); + common->Printf( "Sys_InitInput: Joystic - Found %i joysticks\n", numJoysticks); +#if !SDL_VERSION_ATLEAST(2, 0, 0) + for(i=0; iPrintf(" Joystick %i name '%s'\n", i, SDL_JoystickName(i)); +#endif + + // Open first available joystick and use it + if(SDL_NumJoysticks() > 0) { + joy = SDL_JoystickOpen(0); + + if(joy) { + int num_hats; + + num_hats = SDL_JoystickNumHats(joy); + common->Printf("Opened Joystick number 0\n"); +#if SDL_VERSION_ATLEAST(2, 0, 0) + common->Printf("Name: %s\n", SDL_JoystickName(joy)); +#else + common->Printf("Name: %s\n", SDL_JoystickName(0)); +#endif + common->Printf("Number of Axes: %d\n", SDL_JoystickNumAxes(joy)); + common->Printf("Number of Buttons: %d\n", SDL_JoystickNumButtons(joy)); + common->Printf("Number of Hats: %d\n", num_hats); + common->Printf("Number of Balls: %d\n", SDL_JoystickNumBalls(joy)); + + SDL_joystick_has_hat = 0; + if(num_hats) { + SDL_joystick_has_hat = 1; + } + } + else { + joy = NULL; + common->Printf("Couldn't open Joystick 0\n"); + } + } + else { + joy = NULL; + } + // WM0110 } /* @@ -571,6 +638,15 @@ void Sys_ShutdownInput() { kbd_polls.Clear(); mouse_polls.Clear(); + joystick_polls.Clear(); + + // Close any opened SDL Joystic + if(joy) { + common->Printf("Sys_ShutdownInput: closing SDL joystick.\n"); + SDL_JoystickClose(joy); + } else { + common->Printf("Sys_ShutdownInput: SDL joystick not initialized. Nothing to close.\n"); + } } /* @@ -681,9 +757,11 @@ sysEvent_t Sys_GetEvent() SDL_Event ev; sysEvent_t res = { }; int key; - static const sysEvent_t res_none = { SE_NONE, 0, 0, 0, NULL }; - + + // WM0110: previous state of joystick hat + static int previous_hat_state = SDL_HAT_CENTERED; + #if SDL_VERSION_ATLEAST(2, 0, 0) static char* s = NULL; static size_t s_pos = 0; @@ -870,7 +948,7 @@ sysEvent_t Sys_GetEvent() // DG end #endif - // fall through + // fall through case SDL_KEYUP: { bool isChar; @@ -990,7 +1068,7 @@ sysEvent_t Sys_GetEvent() case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: res.evType = SE_KEY; - + switch( ev.button.button ) { case SDL_BUTTON_LEFT: @@ -1019,15 +1097,264 @@ sysEvent_t Sys_GetEvent() break; #endif } - res.evValue2 = ev.button.state == SDL_PRESSED ? 1 : 0; return res; - + + // WM0110 + // NOTE: it seems that the key bindings for the GUI and for the game are + // totally independant. I think the event returned by this function seems to work + // on the GUI and the event returned by Sys_ReturnJoystickInputEvent() works on + // the game. + // Also, remember that joystick keys must be binded to actions in order to work! + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + // sys_public.h: evValue is an axis number and evValue2 is the current state (-127 to 127) + // WM0110: joystick ranges must be between (-32769, 32768)! + res.evType = SE_KEY; + switch( ev.jbutton.button ) + { + case 0: + res.evValue = K_JOY1; + joystick_polls.Append( joystick_poll_t( J_ACTION1, ev.jbutton.state == SDL_PRESSED ? 1 : 0 ) ); + break; + + case 1: + res.evValue = K_JOY2; + joystick_polls.Append( joystick_poll_t( J_ACTION2, ev.jbutton.state == SDL_PRESSED ? 1 : 0 ) ); + break; + + case 2: + res.evValue = K_JOY3; + joystick_polls.Append( joystick_poll_t( J_ACTION3, ev.jbutton.state == SDL_PRESSED ? 1 : 0 ) ); + break; + + case 3: + res.evValue = K_JOY4; + joystick_polls.Append( joystick_poll_t( J_ACTION4, ev.jbutton.state == SDL_PRESSED ? 1 : 0 ) ); + break; + + case 4: + res.evValue = K_JOY5; + joystick_polls.Append( joystick_poll_t( J_ACTION5, ev.jbutton.state == SDL_PRESSED ? 1 : 0 ) ); + break; + + case 5: + res.evValue = K_JOY6; + joystick_polls.Append( joystick_poll_t( J_ACTION6, ev.jbutton.state == SDL_PRESSED ? 1 : 0 ) ); + break; + + case 6: + res.evValue = K_JOY7; + joystick_polls.Append( joystick_poll_t( J_ACTION7, ev.jbutton.state == SDL_PRESSED ? 1 : 0 ) ); + break; + + case 7: + res.evValue = K_JOY8; + joystick_polls.Append( joystick_poll_t( J_ACTION8, ev.jbutton.state == SDL_PRESSED ? 1 : 0 ) ); + break; + + case 8: + res.evValue = K_JOY9; + joystick_polls.Append( joystick_poll_t( J_ACTION9, ev.jbutton.state == SDL_PRESSED ? 1 : 0 ) ); + break; + + case 9: + res.evValue = K_JOY10; + joystick_polls.Append( joystick_poll_t( J_ACTION10, ev.jbutton.state == SDL_PRESSED ? 1 : 0 ) ); + break; + + case 10: + res.evValue = K_JOY11; + joystick_polls.Append( joystick_poll_t( J_ACTION11, ev.jbutton.state == SDL_PRESSED ? 1 : 0 ) ); + break; + + // D-PAD left (XBox 360 wireless) + case 11: + // If joystick has a hat, then use the hat as D-PAD. If not, D-PAD is mapped + // to buttons. + if(SDL_joystick_has_hat) { + res.evValue = K_JOY12; + joystick_polls.Append( joystick_poll_t( J_ACTION12, ev.jbutton.state == SDL_PRESSED ? 1 : 0 ) ); + } else { + res.evValue = K_JOY_DPAD_LEFT; + joystick_polls.Append( joystick_poll_t( J_DPAD_LEFT, ev.jbutton.state == SDL_PRESSED ? 1 : 0 ) ); + } + break; + + // D-PAD right + case 12: + if(SDL_joystick_has_hat) { + res.evValue = K_JOY13; + joystick_polls.Append( joystick_poll_t( J_ACTION13, ev.jbutton.state == SDL_PRESSED ? 1 : 0 ) ); + } else { + res.evValue = K_JOY_DPAD_RIGHT; + joystick_polls.Append( joystick_poll_t( J_DPAD_RIGHT, ev.jbutton.state == SDL_PRESSED ? 1 : 0 ) ); + } + break; + + // D-PAD up + case 13: + if(SDL_joystick_has_hat) { + res.evValue = K_JOY14; + joystick_polls.Append( joystick_poll_t( J_ACTION14, ev.jbutton.state == SDL_PRESSED ? 1 : 0 ) ); + } else { + res.evValue = K_JOY_DPAD_UP; + joystick_polls.Append( joystick_poll_t( J_DPAD_UP, ev.jbutton.state == SDL_PRESSED ? 1 : 0 ) ); + } + break; + + // D-PAD down + case 14: + if(SDL_joystick_has_hat) { + res.evValue = K_JOY15; + joystick_polls.Append( joystick_poll_t( J_ACTION15, ev.jbutton.state == SDL_PRESSED ? 1 : 0 ) ); + } else { + res.evValue = K_JOY_DPAD_DOWN; + joystick_polls.Append( joystick_poll_t( J_DPAD_DOWN, ev.jbutton.state == SDL_PRESSED ? 1 : 0 ) ); + } + break; + + default: + common->Warning( "Sys_GetEvent(): Unknown joystick button number %i\n", ev.jbutton.button ); + return res_none; + } + res.evValue2 = ev.jbutton.state == SDL_PRESSED ? 1 : 0; + + return res; + + case SDL_JOYHATMOTION: + // If this is not the first hat, ignore this event. + if(ev.jhat.which != 0) + return res_none; + + res.evType = SE_KEY; + if(ev.jhat.value & SDL_HAT_UP) { + res.evValue = K_JOY_DPAD_UP; + joystick_polls.Append( joystick_poll_t( J_DPAD_UP, 1 ) ); + res.evValue2 = 1; + previous_hat_state = J_DPAD_UP; + } + else if (ev.jhat.value & SDL_HAT_DOWN) { + res.evValue = K_JOY_DPAD_DOWN; + joystick_polls.Append( joystick_poll_t( J_DPAD_DOWN, 1 ) ); + res.evValue2 = 1; + previous_hat_state = J_DPAD_DOWN; + } + else if (ev.jhat.value & SDL_HAT_RIGHT) { + res.evValue = K_JOY_DPAD_RIGHT; + joystick_polls.Append( joystick_poll_t( J_DPAD_RIGHT, 1 ) ); + res.evValue2 = 1; + previous_hat_state = J_DPAD_RIGHT; + } + else if (ev.jhat.value & SDL_HAT_LEFT) { + res.evValue = K_JOY_DPAD_LEFT; + joystick_polls.Append( joystick_poll_t( J_DPAD_LEFT, 1 ) ); + res.evValue2 = 1; + previous_hat_state = J_DPAD_LEFT; + } + // SDL_HAT_CENTERED is defined as 0 + else if (ev.jhat.value == SDL_HAT_CENTERED) { + // We need to know the previous state to know which event to send. + if(previous_hat_state == J_DPAD_UP) { + res.evValue = K_JOY_DPAD_UP; + joystick_polls.Append( joystick_poll_t( J_DPAD_UP, 0 ) ); + res.evValue2 = 0; + } + else if(previous_hat_state == J_DPAD_DOWN) { + res.evValue = K_JOY_DPAD_DOWN; + joystick_polls.Append( joystick_poll_t( J_DPAD_DOWN, 0 ) ); + res.evValue2 = 0; + } + else if(previous_hat_state == J_DPAD_RIGHT) { + res.evValue = K_JOY_DPAD_RIGHT; + joystick_polls.Append( joystick_poll_t( J_DPAD_RIGHT, 0 ) ); + res.evValue2 = 0; + } + else if(previous_hat_state == J_DPAD_LEFT) { + res.evValue = K_JOY_DPAD_LEFT; + joystick_polls.Append( joystick_poll_t( J_DPAD_LEFT, 0 ) ); + res.evValue2 = 0; + } + else if(previous_hat_state == SDL_HAT_CENTERED) { + common->Warning( "Sys_GetEvent(): SDL_JOYHATMOTION: previous state SDL_HAT_CENTERED repeated!\n" ); + return res_none; + } + else { + common->Warning( "Sys_GetEvent(): SDL_JOYHATMOTION: unknown previous hat state %i\n", previous_hat_state ); + return res_none; + } + + previous_hat_state = SDL_HAT_CENTERED; + } + else { + common->Warning( "Sys_GetEvent(): Unknown SDL_JOYHATMOTION value %i\n", ev.jhat.value ); + return res_none; + } + + return res; + + case SDL_JOYAXISMOTION: + res.evType = SE_JOYSTICK; + // SDL joystick range is: -32768 to 32767, which is what is expected + // in void idUsercmdGenLocal::Joystick( int deviceNum ). + switch( ev.jaxis.axis ) + { + int trigger_value; + + // LEFT trigger + case 2: + // Convert TRIGGER value from space (-32768, 32767) to (0, 32767) + trigger_value = (ev.jaxis.value + 32768) / 2; + // common->Printf("Sys_GetEvent: LEFT trigger value = %i / converted value = %i\n", ev.jaxis.value, trigger_value); + res.evValue = J_AXIS_LEFT_TRIG; + joystick_polls.Append( joystick_poll_t( J_AXIS_LEFT_TRIG, trigger_value ) ); + break; + + // Right trigger + case 5: + trigger_value = (ev.jaxis.value + 32768) / 2; + // common->Printf("Sys_GetEvent: RIGHT trigger value = %i / converted value = %i\n", ev.jaxis.value, trigger_value); + res.evValue = J_AXIS_RIGHT_TRIG; + joystick_polls.Append( joystick_poll_t( J_AXIS_RIGHT_TRIG, trigger_value ) ); + break; + + // LEFT X + case 0: + res.evValue = J_AXIS_LEFT_X; + joystick_polls.Append( joystick_poll_t( J_AXIS_LEFT_X, ev.jaxis.value ) ); + break; + + // LEFT Y + case 1: + res.evValue = J_AXIS_LEFT_Y; + joystick_polls.Append( joystick_poll_t( J_AXIS_LEFT_Y, ev.jaxis.value ) ); + break; + + // RIGHT X + case 3: + res.evValue = J_AXIS_RIGHT_X; + joystick_polls.Append( joystick_poll_t( J_AXIS_RIGHT_X, ev.jaxis.value ) ); + break; + + // RIGHT Y + case 4: + res.evValue = J_AXIS_RIGHT_Y; + joystick_polls.Append( joystick_poll_t( J_AXIS_RIGHT_Y, ev.jaxis.value ) ); + break; + + default: + common->Warning( "Sys_GetEvent(): Unknown joystick axis number %i\n", ev.jaxis.axis ); + return res_none; + } + + return res; + // WM0110 + case SDL_QUIT: PushConsoleEvent( "quit" ); return res_none; - + case SDL_USEREVENT: switch( ev.user.code ) { @@ -1036,16 +1363,18 @@ sysEvent_t Sys_GetEvent() res.evPtrLength = ( intptr_t )ev.user.data1; res.evPtr = ev.user.data2; return res; + default: - common->Warning( "unknown user event %u", ev.user.code ); + common->Warning( "Sys_GetEvent: unknown SDL_USEREVENT %u", ev.user.code ); return res_none; } + default: - common->Warning( "unknown event %u", ev.type ); + common->Warning( "Sys_GetEvent: unknown SDL event %u", ev.type ); return res_none; } } - + return res_none; } @@ -1150,25 +1479,37 @@ int Sys_PollMouseInputEvents( int mouseEvents[MAX_MOUSE_EVENTS][2] ) void Sys_SetRumble( int device, int low, int hi ) { // TODO; + // SDL 2.0 required (SDL Haptic subsystem) } int Sys_PollJoystickInputEvents( int deviceNum ) { - // TODO; - return 0; + int numEvents = joystick_polls.Num(); + + return numEvents; } - +// This funcion called by void idUsercmdGenLocal::Joystick( int deviceNum ) in +// file UsercmdGen.cpp +// action - must have values belonging to enum sys_jEvents (sys_public.h) +// value - must be 1/0 for button or DPAD pressed/released +// for joystick axes must be in the range (-32769, 32768) +// for joystick trigger must be in the range (0, 32768) int Sys_ReturnJoystickInputEvent( const int n, int& action, int& value ) { - // TODO; - return 0; + // Get last element of the list and copy into argument references + const joystick_poll_t& mp = joystick_polls[n]; + action = mp.action; + value = mp.value; + + return 1; } - +// This funcion called by void idUsercmdGenLocal::Joystick( int deviceNum ) in +// file UsercmdGen.cpp void Sys_EndJoystickInputEvents() { + // Empty the joystick event container. This is called after + // all joystick events have been read using Sys_ReturnJoystickInputEvent() + joystick_polls.SetNum( 0 ); } - - -