//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: Xbox controller implementation for inputsystem.dll // //===========================================================================// #include "inputsystem.h" #ifdef _PS3 #include #include #include #include #include "movecontroller_ps3.h" #include "key_translation.h" #include #endif #include "vstdlib/IKeyValuesSystem.h" #include "materialsystem/imaterialsystem.h" #include "vgui/isurface.h" #include "vgui_controls/controls.h" // NOTE: This has to be the last file included! #include "tier0/memdbgon.h" #include "input_device.h" //----------------------------------------------------------------------------- // Xbox helpers //----------------------------------------------------------------------------- #ifdef PLATFORM_PS3 #define XBX_MAX_MOTOR_SPEED 255 #define PS3_MAX_MOUSE 2 //Max PS3 mouse connections #define PS3_MAX_KEYBOARD 2 //Max PS3 keyboard connections #define PS3_MOUSE_LEFT ( 1<<0 ) #define PS3_MOUSE_RIGHT ( 1<<1 ) #define PS3_MOUSE_WHEEL ( 1<<2 ) #define GetTickCount Plat_MSTime ConVar ps3_joy_ss( "ps3_joy_ss", "0", FCVAR_DEVELOPMENTONLY, "" ); extern ConVar ps3_move_enabled; extern ConVar ps3_move_roll_trigger; #else #define XBX_MAX_MOTOR_SPEED 65535 #endif #define XBX_MAX_BUTTONSAMPLE 32768 #define XBX_MAX_ANALOGSAMPLE 255 #define XBX_MAX_STICKSAMPLE_LEFT 32768 #define XBX_MAX_STICKSAMPLE_RIGHT 32767 #define XBX_MAX_STICKSAMPLE_DOWN 32768 #define XBX_MAX_STICKSAMPLE_UP 32767 #define XBX_STICK_SCALE_LEFT(x) ( ( float )XBX_MAX_STICKSAMPLE_LEFT/( float )( XBX_MAX_STICKSAMPLE_LEFT-(x) ) ) #define XBX_STICK_SCALE_RIGHT(x) ( ( float )XBX_MAX_STICKSAMPLE_RIGHT/( float )( XBX_MAX_STICKSAMPLE_RIGHT-(x) ) ) #define XBX_STICK_SCALE_DOWN(x) ( ( float )XBX_MAX_STICKSAMPLE_DOWN/( float )( XBX_MAX_STICKSAMPLE_DOWN-(x) ) ) #define XBX_STICK_SCALE_UP(x) ( ( float )XBX_MAX_STICKSAMPLE_UP/( float )( XBX_MAX_STICKSAMPLE_UP-(x) ) ) #define XBX_STICK_SMALL_THRESHOLD ((int)( 0.20f * XBX_MAX_STICKSAMPLE_LEFT )) // Threshold for counting analog movement as a button press #define JOYSTICK_ANALOG_BUTTON_THRESHOLD XBX_MAX_STICKSAMPLE_LEFT * 0.4f // Xbox key translation typedef struct { int xinput; int xkey; } xInputToXKey_t; xInputToXKey_t g_digitalXKeyTable[] = { {XINPUT_GAMEPAD_DPAD_UP, XK_BUTTON_UP}, {XINPUT_GAMEPAD_DPAD_DOWN, XK_BUTTON_DOWN}, {XINPUT_GAMEPAD_DPAD_LEFT, XK_BUTTON_LEFT}, {XINPUT_GAMEPAD_DPAD_RIGHT, XK_BUTTON_RIGHT}, {XINPUT_GAMEPAD_START, XK_BUTTON_START}, {XINPUT_GAMEPAD_BACK, XK_BUTTON_BACK}, {XINPUT_GAMEPAD_LEFT_THUMB, XK_BUTTON_STICK1}, {XINPUT_GAMEPAD_RIGHT_THUMB, XK_BUTTON_STICK2}, {XINPUT_GAMEPAD_LEFT_SHOULDER, XK_BUTTON_LEFT_SHOULDER}, {XINPUT_GAMEPAD_RIGHT_SHOULDER, XK_BUTTON_RIGHT_SHOULDER}, {XINPUT_GAMEPAD_A, XK_BUTTON_A}, {XINPUT_GAMEPAD_B, XK_BUTTON_B}, {XINPUT_GAMEPAD_X, XK_BUTTON_X}, {XINPUT_GAMEPAD_Y, XK_BUTTON_Y}, }; #if !defined( _GAMECONSOLE ) typedef DWORD (WINAPI *XInputGetState_t) ( DWORD dwUserIndex, // [in] Index of the gamer associated with the device XINPUT_STATE* pState // [out] Receives the current state ); typedef DWORD (WINAPI *XInputSetState_t) ( DWORD dwUserIndex, // [in] Index of the gamer associated with the device XINPUT_VIBRATION* pVibration // [in, out] The vibration information to send to the controller ); typedef DWORD (WINAPI *XInputGetCapabilities_t) ( DWORD dwUserIndex, // [in] Index of the gamer associated with the device DWORD dwFlags, // [in] Input flags that identify the device type XINPUT_CAPABILITIES* pCapabilities // [out] Receives the capabilities ); XInputGetState_t PC_XInputGetState; XInputSetState_t PC_XInputSetState; XInputGetCapabilities_t PC_XInputGetCapabilities; #define XINPUTGETSTATE PC_XInputGetState #define XINPUTSETSTATE PC_XInputSetState #define XINPUTGETCAPABILITIES PC_XInputGetCapabilities #elif defined( PLATFORM_PS3 ) #define XINPUTGETSTATE PS3_XInputGetState #define XINPUTSETSTATE PS3_XInputSetState #define XINPUTGETCAPABILITIES PS3_XInputGetCapabilities #else #define XINPUTGETSTATE XInputGetState #define XINPUTSETSTATE XInputSetState #define XINPUTGETCAPABILITIES XInputGetCapabilities #endif #if defined( PLATFORM_PS3 ) // PS3 key modifier translation // CTRL/SHIFT/ALT provided as modifiers instead of keycodes, so need a separate table typedef struct { int modifier; ButtonCode_t buttonCode; } ps3ModifierToButtonCode_t; static ps3ModifierToButtonCode_t s_ps3ModifierTable[] = { {CELL_KB_MKEY_L_SHIFT, KEY_LSHIFT}, {CELL_KB_MKEY_R_SHIFT, KEY_RSHIFT}, {CELL_KB_MKEY_L_CTRL, KEY_LCONTROL}, {CELL_KB_MKEY_R_CTRL, KEY_RCONTROL}, {CELL_KB_MKEY_L_ALT, KEY_LALT}, {CELL_KB_MKEY_R_ALT, KEY_RALT} }; static int PS3_SetupKb(int i); static class PS3_XInputInfo_t { public: PS3_XInputInfo_t() { ps3_move_roll_old = 0.0f; ps3_move_rumble_value = 0; ps3_move_rumble_queued = false; } // Global information about all controllers connection and settings state CellPadInfo2 m_CellPadInfo2; float m_flLastConnectedTime[ MAX( XUSER_MAX_COUNT, CELL_PAD_MAX_PORT_NUM ) ]; // Information about each controller button and stick data CellPadData m_CellPadData[ MAX( XUSER_MAX_COUNT, CELL_PAD_MAX_PORT_NUM ) ]; CellPadData m_lastCellPadData[ MAX( XUSER_MAX_COUNT, CELL_PAD_MAX_PORT_NUM ) ]; // Packet number is incremented every time data is successfully obtained from each controller DWORD m_dwPacketNumber[ MAX( XUSER_MAX_COUNT, CELL_PAD_MAX_PORT_NUM ) ]; DWORD m_dwPadPortSettingPressOn; // Global setting whether "ACCEPT" input is CIRCLE button - at the lowest level when // CIRCLE is pressed we return it as A button and when CROSS is pressed as B button bool m_bInputJapaneseSwapAB; // In single-controller mode where multiple controllers can control the game we need // to keep tracking of who is the "active" controller for vibration int m_iActiveSingleControllerIndex; float m_flLastActiveSingleControllerTime; MoveControllerState m_moveControllerState; MoveControllerState m_moveControllerStateOld; // Mouse connections CellMouseInfo m_CellMouseInfo; CellMouseInfo m_CellMouseInfoOld; // Previous mouse status CellMouseData m_CellMouseData[PS3_MAX_MOUSE]; CellMouseData m_CellMouseDataOld[PS3_MAX_MOUSE]; // Keyboard connections CellKbInfo m_CellKbInfo; CellKbInfo m_CellKbInfoOld; // Previous kb status CellKbData m_CellKbData[PS3_MAX_KEYBOARD]; CellKbData m_CellKbDataOld[PS3_MAX_KEYBOARD]; float ps3_move_roll_old; WORD ps3_move_rumble_value; bool ps3_move_rumble_queued; } g_PS3_XInputInfo; float display_aspect_ratio; // [HARDWARE CURSOR] : hardware cursor address in local memory for 64x64x4 argb cursor image (2k aligned) static uint32_t *s_cursorBufferAddress = NULL; // currently active cursor void createCursorImage_SolidColor(void *cursorImage, const uint32_t argbColor) { uint32_t *argb = (uint32_t*)cursorImage; const int w=64, h=64; for (int i=0; i<(w*h); i++) argb[i]=argbColor; } void initCursor() { // load the Prx module needed for cellVideoOutConvertCursorColor int ret = cellSysmoduleLoadModule(CELL_SYSMODULE_AVCONF_EXT); if( ret<0 ) { printf("cellSysmoduleLoadModule(CELL_SYSMODULE_AVCONF_EXT) failed (0x%x)\n", ret); exit(-1); } if ( cellGcmInitCursor() != CELL_OK ) { Msg("Error initializing hardware cursor\n"); } } void CInputSystem::DisableHardwareCursor() { cellGcmSetCursorDisable(); cellGcmUpdateCursor(); } void CInputSystem::ExitHardwareCursor() { DisableHardwareCursor(); cellSysmoduleUnloadModule(CELL_SYSMODULE_AVCONF_EXT); } void loadCursorImage(const void *cursorImage, uint32_t *cursorBufferAddress) { assert(( (intptr_t) cursorBufferAddress % (2*1024) )==0); // must be 2k aligned cellVideoOutConvertCursorColor(CELL_VIDEO_OUT_PRIMARY, CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_X8R8G8B8, 1.0f, CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_X8R8G8B8, (void*)cursorImage, cursorBufferAddress, 64*64); } // set the active hardware cursor address (can only set active on a loaded cursor image) void setActiveCursor( uint32_t *cursorBufferAddress) { assert(( (intptr_t) cursorBufferAddress % (2*1024))==0); // must be 2k aligned uint32_t cursorOffset; cellGcmAddressToOffset( cursorBufferAddress, &cursorOffset ); if( cellGcmSetCursorImageOffset(cursorOffset) != CELL_OK ) { Msg( "Hardware Cursor Error: setting up hardware cursor offset\n" ); } } #if defined( _PS3 ) void CInputSystem::EnableHardwareCursor() { if (cellGcmSetCursorEnable() != CELL_OK ) { Msg( "Hardware Cursor Error: trouble with enable\n" ); } if ( cellGcmUpdateCursor() != CELL_OK ) { Msg( "Hardware Cursor Error: trouble with update\n" ); } } #endif #define CELL_PAD_DEADZONE_PROPORTION 0.2 #define CELL_PAD_ACTIVE_CONTROL_TIME 0.2 void PS3_XInputActiveSingleControllerSet( int iActiveSingleControllerIndex ) { if ( iActiveSingleControllerIndex == g_PS3_XInputInfo.m_iActiveSingleControllerIndex ) { g_PS3_XInputInfo.m_flLastActiveSingleControllerTime = Plat_FloatTime(); return; // remember when was last time of activity on the original controller } // Request is coming in to activate a different controller if ( ( iActiveSingleControllerIndex > 0 ) && ( Plat_FloatTime() - g_PS3_XInputInfo.m_flLastActiveSingleControllerTime < CELL_PAD_ACTIVE_CONTROL_TIME ) ) { return; // let the original controller remain in control } // Deactivate vibration on the previously active controller if ( g_PS3_XInputInfo.m_iActiveSingleControllerIndex > 0 ) { CellPadActParam param; memset( ¶m, 0, sizeof( param ) ); cellPadSetActDirect( g_PS3_XInputInfo.m_iActiveSingleControllerIndex - 1, ¶m ); } g_PS3_XInputInfo.m_iActiveSingleControllerIndex = iActiveSingleControllerIndex; } // [HARDWARE CURSOR] allocate cursor buffer in local memory (multiple of 2k pages, aligned on 2k boundaries) static uint32_t *allocateCursorBuffer() { CellGcmConfig config; cellGcmGetConfiguration(&config); return (uint32_t*)config.localAddress; } void UpdatePS3Mouse( int i ) { int res; CellMouseData msData; res = cellMouseGetData(i, &msData); if (res == CELL_MOUSE_OK) { if(g_PS3_XInputInfo.m_CellMouseData[i].update == CELL_MOUSE_DATA_UPDATE) { // Keep copy of last valid mouse data g_PS3_XInputInfo.m_CellMouseDataOld[i] = g_PS3_XInputInfo.m_CellMouseData[i]; } g_PS3_XInputInfo.m_CellMouseData[i] = msData; } } // sets x and y screen coordinates for mouse and PS move input devices bool CInputSystem::GetPS3CursorPos( int &x, int &y ) { bool result = false; #ifndef _PS3 // only defined under PS3 Assert(0); #endif InputDevice_t currentDevice = GetCurrentInputDevice(); if ( ( IsInputDeviceConnected( INPUT_DEVICE_KEYBOARD_MOUSE ) ) && ( currentDevice == INPUT_DEVICE_KEYBOARD_MOUSE || currentDevice == INPUT_DEVICE_NONE) ) { for(int i=0;iGetBackBufferDimensions( nScreenWidth, nScreenHeight ); x = nScreenWidth * ( ( GetMotionControllerPosX() + 1.0f ) * 0.5f ); y = nScreenHeight * ( 1.0 - ( ( GetMotionControllerPosY() + 1.0f ) * 0.5f ) ); if ( vgui::surface()->IsCursorVisible() ) { PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_XY, x, y ); } result = true; } return result; } void CInputSystem::PS3SetupHardwareCursor( void* image ) { if( image == NULL ) { // [dkorus] using our old default block cursor for testing! uint32_t solidCursorImage[64*64]; // original cursor images: image = solidCursorImage; createCursorImage_SolidColor( image, 0xa0ffffff ); } s_cursorBufferAddress = allocateCursorBuffer(); initCursor(); loadCursorImage( image, s_cursorBufferAddress ); setActiveCursor( s_cursorBufferAddress ); } void CInputSystem::PS3_PollMouse() { int res; CellMouseData msData; CellMouseInfo* pMsInfo = &g_PS3_XInputInfo.m_CellMouseInfo; CellMouseInfo* pMsInfoOld = &g_PS3_XInputInfo.m_CellMouseInfoOld; *pMsInfoOld = *pMsInfo; if (CELL_OK != (res = cellMouseGetInfo(pMsInfo))) { Msg("Error%08X : cellMouseGetInfo\n", res); } else { // Check info field for monitoring the INTERCEPTED state (when system grabs ownership of the mouse data). bool bIntercepted = pMsInfo->info & CELL_MOUSE_INFO_INTERCEPTED; if( bIntercepted && (!(pMsInfoOld->info & CELL_MOUSE_INFO_INTERCEPTED))) { Msg("Lost the ownership of the mouse data\n"); } else if(!bIntercepted && (pMsInfoOld->info & CELL_MOUSE_INFO_INTERCEPTED)) { Msg("Regained ownership of the mouse data\n"); for(int i=0;istatus[i] == CELL_MOUSE_STATUS_CONNECTED) { if (pMsInfoOld->status[i] == CELL_MOUSE_STATUS_DISCONNECTED) { Msg("Mouse[%d] connected.\n", i); cellMouseClearBuf(i); // testing the hardware cursor m_PS3MouseConnected = true; EnableHardwareCursor(); } UpdatePS3Mouse( i ); } else if(pMsInfo->status[i] == CELL_MOUSE_STATUS_DISCONNECTED) { if(pMsInfoOld->status[i] == CELL_MOUSE_STATUS_CONNECTED) { // call these when we're done: // disableCursor(); // exitCursor(); Msg("Mouse[%d] disconnected.\n", i); m_PS3MouseConnected = false; // ? } } SetInputDeviceConnected( INPUT_DEVICE_KEYBOARD_MOUSE, ( m_PS3KeyboardConnected && m_PS3MouseConnected ) ); } } } } void CInputSystem::PS3_PollKeyboard() { int res; CellKbData kbData; CellKbInfo* pKbInfo = &g_PS3_XInputInfo.m_CellKbInfo; CellKbInfo* pKbInfoOld = &g_PS3_XInputInfo.m_CellKbInfoOld; *pKbInfoOld = *pKbInfo; if (CELL_OK != (res = cellKbGetInfo(pKbInfo))) { Msg("Error%08X : cellKbGetInfo\n", res); } else { // Check info field for monitoring the INTERCEPTED state (when system grabs ownership of the keyboard data). bool bIntercepted = pKbInfo->info & CELL_KB_INFO_INTERCEPTED; if( bIntercepted && (!(pKbInfoOld->info & CELL_KB_INFO_INTERCEPTED))) { Msg("Lost the ownership of the keyboard data\n"); } else if(!bIntercepted && (pKbInfoOld->info & CELL_KB_INFO_INTERCEPTED)) { Msg("Regained ownership of the keyboard data\n"); for(int i=0;istatus[i] == CELL_KB_STATUS_CONNECTED) { if (pKbInfoOld->status[i] == CELL_KB_STATUS_DISCONNECTED) { Msg("Keyboard[%d] connected.\n", i); m_PS3KeyboardConnected = true; PS3_SetupKb(i); } res = cellKbRead(i, &kbData); if (res == CELL_KB_OK) { // Keep copy of last valid keyboard data if(g_PS3_XInputInfo.m_CellKbData[i].len > 0) { g_PS3_XInputInfo.m_CellKbDataOld[i] = g_PS3_XInputInfo.m_CellKbData[i]; } g_PS3_XInputInfo.m_CellKbData[i] = kbData; } } else if(pKbInfo->status[i] == CELL_KB_STATUS_DISCONNECTED) { if(pKbInfoOld->status[i] == CELL_KB_STATUS_CONNECTED) { Msg("Keyboard[%d] disconnected.\n", i); m_PS3KeyboardConnected = false; // ? } } SetInputDeviceConnected( INPUT_DEVICE_KEYBOARD_MOUSE, ( m_PS3KeyboardConnected && m_PS3MouseConnected ) ); } } } } bool HandleMoveControllerSelectButtonHack( CellPadData &data, int controllerIndex ) { // [dkorus] hacky solution to let steam overlay open with SELECT button from the last frame's input bool usingMoveControllerSelectBtn = (g_PS3_XInputInfo.m_moveControllerStateOld.m_aCellGemState[0].pad.digitalbuttons & CELL_GEM_CTRL_SELECT); // need to track whether we were using the move controller select button last time we ran this code static bool wasUsingMoveControllerSelectBtn = false; if ( usingMoveControllerSelectBtn || wasUsingMoveControllerSelectBtn ) { if ( data.len <= 0 ) { // fill in valid data from our last successful pad sample data = g_PS3_XInputInfo.m_lastCellPadData[ controllerIndex ]; } } if ( usingMoveControllerSelectBtn ) { // force in a button value for select data.button[ CELL_PAD_BTN_OFFSET_DIGITAL1 ] |= CELL_PAD_CTRL_SELECT; // give a valid length (this is just the value in the data structure when SELECT is normally pressed) data.len = MAX(20, data.len); } else if ( wasUsingMoveControllerSelectBtn ) { // force in a button value for select data.button[ CELL_PAD_BTN_OFFSET_DIGITAL1 ] &= ~(CELL_PAD_CTRL_SELECT); // give a valid length (this is just the value in the data structure when SELECT is normally pressed) data.len = MAX(20,data.len); } wasUsingMoveControllerSelectBtn = usingMoveControllerSelectBtn; return usingMoveControllerSelectBtn; } void CInputSystem::PS3_XInputPollEverything( BCellPadDataHook_t hookFunc, BCellPadNoDataHook_t hookNoDataFunc ) { memset( &g_PS3_XInputInfo.m_CellPadInfo2, 0, sizeof( g_PS3_XInputInfo.m_CellPadInfo2 ) ); int res = cellPadGetInfo2( &g_PS3_XInputInfo.m_CellPadInfo2 ); if ( res < CELL_OK ) { if ( hookNoDataFunc ) hookNoDataFunc(); PS3_XInputActiveSingleControllerSet( 0 ); return; } if ( g_PS3_XInputInfo.m_CellPadInfo2.system_info & CELL_PAD_INFO_INTERCEPTED ) { // OS is intercepting all controller input if ( hookNoDataFunc ) hookNoDataFunc(); PS3_XInputActiveSingleControllerSet( 0 ); return; } int iActiveControllerDetected = ( XBX_GetNumGameUsers() == 1 ) ? 0 : -1; float flCurrentTime = Plat_FloatTime(); for ( int k = 0; k < ARRAYSIZE( g_PS3_XInputInfo.m_CellPadInfo2.port_status ); ++ k ) { if ( g_PS3_XInputInfo.m_CellPadInfo2.port_status[k] & CELL_PAD_STATUS_CONNECTED ) { if ( 0 == ( g_PS3_XInputInfo.m_dwPadPortSettingPressOn & ( 1 << k ) ) ) { res = cellPadSetPortSetting( k, CELL_PAD_SETTING_PRESS_ON ); // Set pad to all-analog mode if ( res == CELL_PAD_OK ) { g_PS3_XInputInfo.m_CellPadInfo2.port_setting[k] |= CELL_PAD_SETTING_PRESS_ON; g_PS3_XInputInfo.m_dwPadPortSettingPressOn |= ( 1 << k ); } } CellPadData data; res = cellPadGetData( k, &data ); // [dkorus] this func checks/adds select button data from the move controller to the CellPadData we're passing through. Used for the steam overlay on PS3 // please remove when steam overlay is reworked to correctly sample move controller data! HandleMoveControllerSelectButtonHack( data, k ); if ( res < CELL_OK ) { // Failed to obtain data - mark ctrlr as disconnected! g_PS3_XInputInfo.m_CellPadInfo2.port_status[k] &=~ CELL_PAD_STATUS_CONNECTED; } else if ( data.len > 0 ) { // New data obtained! bool bDiscard = false; if ( hookFunc ) { bDiscard = hookFunc( data ); } if ( !bDiscard ) { Q_memcpy( &g_PS3_XInputInfo.m_CellPadData[k], &data, sizeof( CellPadData ) ); ++ g_PS3_XInputInfo.m_dwPacketNumber[k]; if ( ( g_PS3_XInputInfo.m_iActiveSingleControllerIndex - 1 == k ) || !iActiveControllerDetected ) iActiveControllerDetected = k + 1; } g_PS3_XInputInfo.m_lastCellPadData[k] = data; } } if ( ( g_PS3_XInputInfo.m_CellPadInfo2.port_status[k] & CELL_PAD_STATUS_CONNECTED ) != CELL_PAD_STATUS_CONNECTED ) { // PS3 controllers can get disconnected for brief periods of time // when they transition between charging and wireless modes :( if ( flCurrentTime - g_PS3_XInputInfo.m_flLastConnectedTime[k] > CELL_PAD_ACTIVE_CONTROL_TIME ) { // Controller is treated as disconnected, reset data state memset( &g_PS3_XInputInfo.m_CellPadData[k], 0, sizeof( CellPadData ) ); } else { // Pretend like it's still connected, just no state changes g_PS3_XInputInfo.m_CellPadInfo2.port_status[k] |= CELL_PAD_STATUS_CONNECTED; } } else { // Remember when this controller was last connected g_PS3_XInputInfo.m_flLastConnectedTime[k] = flCurrentTime; } } if(g_pMoveController->m_bEnabled) { g_PS3_XInputInfo.m_moveControllerStateOld = g_PS3_XInputInfo.m_moveControllerState; g_pMoveController->ReadState(&g_PS3_XInputInfo.m_moveControllerState); } PS3_PollMouse(); PS3_PollKeyboard(); if ( iActiveControllerDetected ) { PS3_XInputActiveSingleControllerSet( iActiveControllerDetected ); } } // [dkorus] mode masks and button masks for sharpshooter straight from sharpshooter sdk demo #define CUSTOM0_MODE_1 (1 << 0) #define CUSTOM0_MODE_2 (1 << 1) #define CUSTOM0_MODE_3 (1 << 2) #define CUSTOM0_MODE_MASK (CUSTOM0_MODE_1 | CUSTOM0_MODE_2 | CUSTOM0_MODE_3) #define CUSTOM0_T_BUTTON_TRIGGER (1 << 6) #define CUSTOM0_RL_RELOAD_BUTTON (1 << 7) class CSharpshooterButtonData { public: CSharpshooterButtonData() { prev_mode = 0; m_reloadPressedLastFrame = false; m_triggerPressedLastFrame = false; m_pumpActionPressedLastFrame = false; } unsigned char prev_mode; bool m_reloadPressedLastFrame; bool m_triggerPressedLastFrame; bool m_pumpActionPressedLastFrame; }; CSharpshooterButtonData g_sharpshooterData; void CInputSystem::HandlePS3SharpshooterButtons( void ) { // we have a sharpshooter, read in the special buttons static const unsigned char num_dots_lookup[8] = {0, 1, 2, 0, 3, 0, 0, 0}; unsigned char mode = num_dots_lookup[g_PS3_XInputInfo.m_moveControllerState.m_aCellGemState[0].ext.custom[0] & CUSTOM0_MODE_MASK]; if ( mode != g_sharpshooterData.prev_mode ) { // fire mode selector changed ButtonCode_t prev_mode_button = KEY_XBUTTON_FIREMODE_SELECTOR_1; switch ( g_sharpshooterData.prev_mode ) { case 1: prev_mode_button = KEY_XBUTTON_FIREMODE_SELECTOR_1; break; case 2: prev_mode_button = KEY_XBUTTON_FIREMODE_SELECTOR_2; break; case 3: prev_mode_button = KEY_XBUTTON_FIREMODE_SELECTOR_3; break; default: Msg( "Button prev_mode %d unhandled\n",g_sharpshooterData.prev_mode ); }; PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, prev_mode_button, prev_mode_button ); ButtonCode_t new_mode_button = KEY_XBUTTON_FIREMODE_SELECTOR_1; switch ( mode ) { case 1: new_mode_button = KEY_XBUTTON_FIREMODE_SELECTOR_1; break; case 2: new_mode_button = KEY_XBUTTON_FIREMODE_SELECTOR_2; break; case 3: new_mode_button = KEY_XBUTTON_FIREMODE_SELECTOR_3; break; default: Msg( "Button mode %d unhandled\n",mode ); }; PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, new_mode_button, new_mode_button ); g_sharpshooterData.prev_mode = mode; } if ( g_PS3_XInputInfo.m_moveControllerState.m_aCellGemState[0].ext.custom[0] & CUSTOM0_RL_RELOAD_BUTTON ) { // reload button pressed if ( !g_sharpshooterData.m_reloadPressedLastFrame ) { PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, KEY_XBUTTON_RELOAD, KEY_XBUTTON_RELOAD ); } g_sharpshooterData.m_reloadPressedLastFrame = true; } else { if ( g_sharpshooterData.m_reloadPressedLastFrame ) { PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, KEY_XBUTTON_RELOAD, KEY_XBUTTON_RELOAD ); } g_sharpshooterData.m_reloadPressedLastFrame = false; } if ( g_PS3_XInputInfo.m_moveControllerState.m_aCellGemState[0].ext.custom[0] & CUSTOM0_T_BUTTON_TRIGGER ) // custom T button trigger takes priority over actual Move T button { // trigger pressed if ( !g_sharpshooterData.m_triggerPressedLastFrame ) { if( m_setCurrentInputDeviceOnNextButtonPress ) { // [dkorus] since this is a move specific button, it's either triggered by the move or the sharpshooter if ( IsInputDeviceConnected( INPUT_DEVICE_SHARPSHOOTER ) ) SetCurrentInputDevice( INPUT_DEVICE_SHARPSHOOTER ); else SetCurrentInputDevice( INPUT_DEVICE_PLAYSTATION_MOVE ); m_setCurrentInputDeviceOnNextButtonPress = false; } PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, KEY_XBUTTON_TRIGGER, KEY_XBUTTON_TRIGGER ); } g_sharpshooterData.m_triggerPressedLastFrame = true; } else { if ( g_sharpshooterData.m_triggerPressedLastFrame ) { PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, KEY_XBUTTON_TRIGGER, KEY_XBUTTON_TRIGGER ); } g_sharpshooterData.m_triggerPressedLastFrame = false; if ( g_PS3_XInputInfo.m_moveControllerState.m_aCellGemState[0].pad.digitalbuttons & CELL_GEM_CTRL_T ) { if ( !g_sharpshooterData.m_pumpActionPressedLastFrame ) { // pump action grip pulled PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, KEY_XBUTTON_PUMP_ACTION, KEY_XBUTTON_PUMP_ACTION ); } g_sharpshooterData.m_pumpActionPressedLastFrame = true; } else { if ( g_sharpshooterData.m_pumpActionPressedLastFrame ) { PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, KEY_XBUTTON_PUMP_ACTION, KEY_XBUTTON_PUMP_ACTION ); } g_sharpshooterData.m_pumpActionPressedLastFrame = false; } } } void CInputSystem::HandlePS3Move( PXINPUT_STATE& pState ) { // handling controller buttons CellGemPadData gemPadDataOld = g_PS3_XInputInfo.m_moveControllerStateOld.m_aCellGemState[0].pad; CellGemPadData gemPadData = g_PS3_XInputInfo.m_moveControllerState.m_aCellGemState[0].pad; if ( IsDeviceReadingInput( INPUT_DEVICE_SHARPSHOOTER ) && IsInputDeviceConnected( INPUT_DEVICE_SHARPSHOOTER ) ) { HandlePS3SharpshooterButtons( ); } // check for queued rumble if ( g_PS3_XInputInfo.ps3_move_rumble_queued ) { g_pMoveController->Rumble( g_PS3_XInputInfo.ps3_move_rumble_value ); g_PS3_XInputInfo.ps3_move_rumble_queued = false; } if (gemPadData.digitalbuttons & CELL_GEM_CTRL_START) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_START; if (gemPadData.digitalbuttons & CELL_GEM_CTRL_SELECT) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_BACK; if ( (gemPadData.digitalbuttons & CELL_GEM_CTRL_CROSS) || ( gemPadData.analog_T>0 && !g_sharpshooterData.m_pumpActionPressedLastFrame ) ) { if( m_setCurrentInputDeviceOnNextButtonPress ) { if ( IsInputDeviceConnected( INPUT_DEVICE_SHARPSHOOTER ) ) SetCurrentInputDevice( INPUT_DEVICE_SHARPSHOOTER ); else SetCurrentInputDevice( INPUT_DEVICE_PLAYSTATION_MOVE ); m_setCurrentInputDeviceOnNextButtonPress = false; } } if ( ( gemPadData.digitalbuttons & CELL_GEM_CTRL_CROSS ) ) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_A; if (gemPadData.digitalbuttons & CELL_GEM_CTRL_CIRCLE) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_B; if (gemPadData.digitalbuttons & CELL_GEM_CTRL_TRIANGLE) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_Y; if (gemPadData.digitalbuttons & CELL_GEM_CTRL_SQUARE) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_X; if ( !IsInputDeviceConnected( INPUT_DEVICE_SHARPSHOOTER ) ) { if ( gemPadData.analog_T>0 && !g_sharpshooterData.m_pumpActionPressedLastFrame ) { if ( gemPadDataOld.analog_T <= 0 ) { PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, KEY_XBUTTON_TRIGGER, KEY_XBUTTON_TRIGGER ); } } else if ( gemPadDataOld.analog_T > 0) { PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, KEY_XBUTTON_TRIGGER, KEY_XBUTTON_TRIGGER ); } } gemPadDataOld.analog_T = gemPadData.analog_T; // Get roll from Quaternion Quaternion qMc = Quaternion(-g_PS3_XInputInfo.m_moveControllerState.m_aCellGemState[0].quat[2], -g_PS3_XInputInfo.m_moveControllerState.m_aCellGemState[0].quat[0], g_PS3_XInputInfo.m_moveControllerState.m_aCellGemState[0].quat[1], g_PS3_XInputInfo.m_moveControllerState.m_aCellGemState[0].quat[3]); float fRoll = 0.0f; float m23 = ( 2.0f * qMc.y * qMc.z ) + ( 2.0f * qMc.w * qMc.x ); float m33 = ( 2.0f * qMc.w * qMc.w ) + ( 2.0f * qMc.z * qMc.z ) - 1.0f; if(!(m23==0 && m33==0)) { fRoll = RAD2DEG(atan2(m23,m33)); } float fRollTrigger = ps3_move_roll_trigger.GetFloat(); if ( fRoll > fRollTrigger ) { if ( g_PS3_XInputInfo.ps3_move_roll_old <= fRollTrigger ) { PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, KEY_XBUTTON_ROLL_RIGHT, KEY_XBUTTON_ROLL_RIGHT ); } } else if ( g_PS3_XInputInfo.ps3_move_roll_old > fRollTrigger ) { PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, KEY_XBUTTON_ROLL_RIGHT, KEY_XBUTTON_ROLL_RIGHT ); } if(fRoll < (fRollTrigger*-1.0f)) { if ( g_PS3_XInputInfo.ps3_move_roll_old >= ( fRollTrigger * -1.0f ) ) { PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, KEY_XBUTTON_ROLL_LEFT, KEY_XBUTTON_ROLL_LEFT ); } } else if ( g_PS3_XInputInfo.ps3_move_roll_old < ( fRollTrigger * -1.0f ) ) { PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, KEY_XBUTTON_ROLL_LEFT, KEY_XBUTTON_ROLL_LEFT ); } g_PS3_XInputInfo.ps3_move_roll_old = fRoll; } DWORD CInputSystem::PS3_XInputGetState( DWORD dwUserIndex, PXINPUT_STATE pState ) { if ( !( g_PS3_XInputInfo.m_CellPadInfo2.port_status[dwUserIndex] & CELL_PAD_STATUS_CONNECTED ) ) return ERROR_DEVICE_NOT_CONNECTED; CellPadData const padData = g_PS3_XInputInfo.m_CellPadData[ dwUserIndex ]; // Convert cellPad state to Valve (x360) gamepad state memset(&pState->Gamepad.wButtons, 0, sizeof(pState->Gamepad.wButtons)); if ( !IsDeviceReadingInput( INPUT_DEVICE_GAMEPAD ) && !IsDeviceReadingInput( INPUT_DEVICE_PLAYSTATION_MOVE ) && !IsDeviceReadingInput( INPUT_DEVICE_SHARPSHOOTER ) && !IsDeviceReadingInput( INPUT_DEVICE_MOVE_NAV_CONTROLLER ) ) { return ERROR_SUCCESS; } // Analog-only cell pad buttons // pState->Gamepad.sThumbLX = (short)( ( (unsigned int) ( ( ( short) padData.button[ CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X ] ) << 8 ) - 1 ) - 0x7fff ); // pState->Gamepad.sThumbLY = (short)( ( (unsigned int) ( ( ( short) padData.button[ CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y ] ) << 8 ) - 1 ) - 0x7fff ); // pState->Gamepad.sThumbRX = (short)( ( (unsigned int) ( ( ( short) padData.button[ CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X ] ) << 8 ) - 1 ) - 0x7fff ); // pState->Gamepad.sThumbRY = (short)( ( (unsigned int) ( ( ( short) padData.button[ CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y ] ) << 8 ) - 1 ) - 0x7fff ); int lx,ly,rx,ry; lx = padData.button[CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X] &0xff; ly = padData.button[CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y] &0xff; rx = 0; ry = 0; // don't use right stick input when using move controller. if ( IsDeviceReadingInput( INPUT_DEVICE_GAMEPAD ) || IsDeviceReadingInput( INPUT_DEVICE_MOVE_NAV_CONTROLLER ) ) { rx = padData.button[CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X] &0xff; ry = padData.button[CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y] &0xff; rx = (rx * 65535) / 255; ry = (ry * 65535) / 255; rx -= 0x8000; ry = 0x7FFF - ry; } lx = (lx * 65535) / 255; ly = (ly * 65535) / 255; lx -= 0x8000; ly = 0x7FFF - ly; pState->Gamepad.sThumbLX = (signed short)lx; pState->Gamepad.sThumbLY = (signed short)ly; pState->Gamepad.sThumbRX = (signed short)rx; pState->Gamepad.sThumbRY = (signed short)ry; // Digital-only cell pad buttons if (padData.button[CELL_PAD_BTN_OFFSET_DIGITAL1] & CELL_PAD_CTRL_START) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_START; if (padData.button[CELL_PAD_BTN_OFFSET_DIGITAL1] & CELL_PAD_CTRL_SELECT) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_BACK; if (padData.button[CELL_PAD_BTN_OFFSET_DIGITAL1] & CELL_PAD_CTRL_L3) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_THUMB; if (padData.button[CELL_PAD_BTN_OFFSET_DIGITAL1] & CELL_PAD_CTRL_R3) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_THUMB; // Buttons which can be either analog or digital bool bAllAnalog = !!( g_PS3_XInputInfo.m_CellPadInfo2.port_setting[ dwUserIndex ] & CELL_PAD_SETTING_PRESS_ON ); if (bAllAnalog) { if (padData.button[CELL_PAD_BTN_OFFSET_PRESS_LEFT] > CELL_PAD_DEADZONE_PROPORTION) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_LEFT; if (padData.button[CELL_PAD_BTN_OFFSET_PRESS_UP] > CELL_PAD_DEADZONE_PROPORTION) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_UP; if (padData.button[CELL_PAD_BTN_OFFSET_PRESS_DOWN] > CELL_PAD_DEADZONE_PROPORTION) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_DOWN; if (padData.button[CELL_PAD_BTN_OFFSET_PRESS_RIGHT] > CELL_PAD_DEADZONE_PROPORTION) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT; if (padData.button[CELL_PAD_BTN_OFFSET_PRESS_CROSS] > CELL_PAD_DEADZONE_PROPORTION) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_A; if (padData.button[CELL_PAD_BTN_OFFSET_PRESS_CIRCLE] > CELL_PAD_DEADZONE_PROPORTION) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_B; if (padData.button[CELL_PAD_BTN_OFFSET_PRESS_TRIANGLE] > CELL_PAD_DEADZONE_PROPORTION) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_Y; if (padData.button[CELL_PAD_BTN_OFFSET_PRESS_SQUARE] > CELL_PAD_DEADZONE_PROPORTION) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_X; if (padData.button[CELL_PAD_BTN_OFFSET_PRESS_L1] > CELL_PAD_DEADZONE_PROPORTION) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_SHOULDER; if (padData.button[CELL_PAD_BTN_OFFSET_PRESS_R1] > CELL_PAD_DEADZONE_PROPORTION) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_SHOULDER; pState->Gamepad.bLeftTrigger = padData.button[CELL_PAD_BTN_OFFSET_PRESS_L2]; pState->Gamepad.bRightTrigger = padData.button[CELL_PAD_BTN_OFFSET_PRESS_R2]; } else { if (padData.button[CELL_PAD_BTN_OFFSET_DIGITAL1] & CELL_PAD_CTRL_LEFT) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_LEFT; if (padData.button[CELL_PAD_BTN_OFFSET_DIGITAL1] & CELL_PAD_CTRL_UP) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_UP; if (padData.button[CELL_PAD_BTN_OFFSET_DIGITAL1] & CELL_PAD_CTRL_DOWN) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_DOWN; if (padData.button[CELL_PAD_BTN_OFFSET_DIGITAL1] & CELL_PAD_CTRL_RIGHT) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT; if (padData.button[CELL_PAD_BTN_OFFSET_DIGITAL2] & CELL_PAD_CTRL_CROSS) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_A; if (padData.button[CELL_PAD_BTN_OFFSET_DIGITAL2] & CELL_PAD_CTRL_CIRCLE) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_B; if (padData.button[CELL_PAD_BTN_OFFSET_DIGITAL2] & CELL_PAD_CTRL_TRIANGLE) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_Y; if (padData.button[CELL_PAD_BTN_OFFSET_DIGITAL2] & CELL_PAD_CTRL_SQUARE) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_X; if (padData.button[CELL_PAD_BTN_OFFSET_DIGITAL2] & CELL_PAD_CTRL_L1) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_SHOULDER; if (padData.button[CELL_PAD_BTN_OFFSET_DIGITAL2] & CELL_PAD_CTRL_R1) pState->Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_SHOULDER; if (padData.button[CELL_PAD_BTN_OFFSET_DIGITAL2] & CELL_PAD_CTRL_L2) pState->Gamepad.bLeftTrigger = 0xFF; else pState->Gamepad.bLeftTrigger = 0x00; if (padData.button[CELL_PAD_BTN_OFFSET_DIGITAL2] & CELL_PAD_CTRL_R2) { pState->Gamepad.bRightTrigger = 0xFF; } else { pState->Gamepad.bRightTrigger = 0x00; } } if ( g_PS3_XInputInfo.m_bInputJapaneseSwapAB ) { pState->Gamepad.wButtons = ( pState->Gamepad.wButtons & ~( XINPUT_GAMEPAD_A | XINPUT_GAMEPAD_B ) ) | ( ( pState->Gamepad.wButtons & XINPUT_GAMEPAD_A ) ? XINPUT_GAMEPAD_B : 0 ) | ( ( pState->Gamepad.wButtons & XINPUT_GAMEPAD_B ) ? XINPUT_GAMEPAD_A : 0 ); } pState->dwPacketNumber = g_PS3_XInputInfo.m_dwPacketNumber[dwUserIndex]; // Add motion controller button states (overwrite gamepad data) // If motion controller connected and calibrated... if( g_pMoveController->m_bEnabled && (g_PS3_XInputInfo.m_moveControllerState.m_CellGemInfo.status[0]==CELL_GEM_STATUS_READY) && (g_PS3_XInputInfo.m_moveControllerState.m_aStatus[0] == CELL_OK) && ( IsDeviceReadingInput( INPUT_DEVICE_PLAYSTATION_MOVE ) || IsDeviceReadingInput( INPUT_DEVICE_SHARPSHOOTER ) ) ) { // [dkorus] pState passed by reference for ease of use, it may change inside of this func HandlePS3Move( pState ); } return ERROR_SUCCESS; } DWORD PS3_XInputSetState( DWORD dwUserIndex, PXINPUT_VIBRATION pVibration ) { CellPadActParam param; memset( ¶m, 0, sizeof( param ) ); if ( XBX_GetNumGameUsers() ) { if ( g_PS3_XInputInfo.m_iActiveSingleControllerIndex > 0 ) // single player vibration override dwUserIndex = g_PS3_XInputInfo.m_iActiveSingleControllerIndex - 1; param.motor[0] = !!pVibration->wLeftMotorSpeed; param.motor[1] = pVibration->wRightMotorSpeed; } cellPadSetActDirect( dwUserIndex, ¶m ); return ERROR_SUCCESS; } DWORD PS3_XInputGetCapabilities( DWORD dwUserIndex, DWORD dwFlags, PXINPUT_CAPABILITIES pCapabilities ) { // [dkorus] in the future it would make sense to move PS3 device information here // note, this might be complicated for some devices as some input types have their capabilitys change on the fly // for exmaple, a standard controller has rumble, but it's disabled if the move controller is set to the // current controller. pCapabilities->Type = XINPUT_DEVTYPE_GAMEPAD; pCapabilities->SubType = XINPUT_DEVSUBTYPE_GAMEPAD; pCapabilities->Flags = 0; return ( g_PS3_XInputInfo.m_CellPadInfo2.port_status[dwUserIndex] & CELL_PAD_STATUS_CONNECTED ) ? ERROR_SUCCESS : ERROR_DEVICE_NOT_CONNECTED; } void PS3_XInputShutdown() { cellPadEnd(); g_pMoveController->Shutdown(); cellMouseEnd(); cellKbEnd(); } //-------------------------------------------------------------------------------------------------- // PS3_InitMouse // Init the PS3 mouse library //-------------------------------------------------------------------------------------------------- static int PS3_InitMouse() { int ret = cellMouseInit(PS3_MAX_MOUSE); if (ret != CELL_MOUSE_OK) { Msg("Error(%08X) : PS3 cellMouseInit error\n", ret); return (ret); } return (CELL_OK); } //-------------------------------------------------------------------------------------------------- // PS3_SetupKb // Setup initial keyboard state //-------------------------------------------------------------------------------------------------- static int PS3_SetupKb(int i) { int ret = cellKbSetLEDStatus(i, CELL_KB_LED_NUM_LOCK | CELL_KB_LED_CAPS_LOCK | CELL_KB_LED_SCROLL_LOCK ); if (ret != CELL_KB_OK) { Msg("Error(%08X) : cellKbSetLEDStatus, kb no = %d\n", ret, i); } ret = cellKbSetReadMode(i, CELL_KB_RMODE_PACKET); if (ret != CELL_KB_OK) { Msg("Error(%08X) : cellKbSetReadMode, kb no = %d\n", ret, i); cellKbEnd(); return (ret); } ret = cellKbSetCodeType(i, CELL_KB_CODETYPE_RAW); if (ret != CELL_KB_OK) { Msg("Error(%08X) : cellKbSetCodeType, kb no = %d\n", ret, i); cellKbEnd(); return (ret); } ret = cellKbClearBuf(i); if (ret != CELL_KB_OK) { Msg("Error(%08X) : cellKbClearBuf, kb no = %d\n", ret, i); cellKbEnd(); return (ret); } return ret; } //-------------------------------------------------------------------------------------------------- // PS3_InitKb // Init the PS3 keyboard library //-------------------------------------------------------------------------------------------------- static int PS3_InitKb() { int ret = cellKbInit(PS3_MAX_KEYBOARD); if (ret != CELL_KB_OK) { Msg("Error(%08X) : PS3 cellKbInit error\n", ret); return (ret); } CellKbInfo kbInfo; ret = cellKbGetInfo(&kbInfo); if (ret != CELL_KB_OK) { Msg("Error%08X : cellKbGetInfo\n", ret); return (ret); } if(!(kbInfo.info & CELL_KB_INFO_INTERCEPTED)) { for (int i = 0; i < PS3_MAX_KEYBOARD; i++) { if(kbInfo.status[i]==CELL_KB_STATUS_CONNECTED) { PS3_SetupKb(i); } } } return (CELL_OK); } #endif //----------------------------------------------------------------------------- // Purpose: Initialize all Xbox controllers //----------------------------------------------------------------------------- void CInputSystem::InitializeXDevices( void ) { bool bInputSwapAB = false; #ifdef PLATFORM_PS3 memset( &g_PS3_XInputInfo, 0, sizeof( g_PS3_XInputInfo ) ); cellPadInit( XUSER_MAX_COUNT ); int bEnterAssignment = CELL_SYSUTIL_ENTER_BUTTON_ASSIGN_CROSS; // default to western standard assignment if ( CELL_OK == cellSysutilGetSystemParamInt( CELL_SYSUTIL_SYSTEMPARAM_ID_ENTER_BUTTON_ASSIGN, &bEnterAssignment ) && bEnterAssignment == CELL_SYSUTIL_ENTER_BUTTON_ASSIGN_CIRCLE ) { g_PS3_XInputInfo.m_bInputJapaneseSwapAB = true; bInputSwapAB = true; } // Init Move Controller g_pMoveController->Init(); // Init mouse lib PS3_InitMouse(); // Init keyboard lib PS3_InitKb(); #endif KeyValuesSystem()->SetKeyValuesExpressionSymbol( "INPUTSWAPAB", bInputSwapAB ); int i; xdevice_t* pXDevice; // assume no joystick m_nJoystickCount = 0; #if !defined( _GAMECONSOLE ) PC_XInputGetState = (XInputGetState_t)GetProcAddress( (HMODULE)m_pXInputDLL, "XInputGetState" ); PC_XInputSetState = (XInputSetState_t)GetProcAddress( (HMODULE)m_pXInputDLL, "XInputSetState" ); PC_XInputGetCapabilities = (XInputGetCapabilities_t)GetProcAddress( (HMODULE)m_pXInputDLL, "XInputGetCapabilities" ); if ( !PC_XInputGetState || !PC_XInputSetState || !PC_XInputGetCapabilities ) return; #endif // query gamepads pXDevice = m_XDevices; for ( i = 0; i < XUSER_MAX_COUNT; ++i, ++pXDevice ) { OpenXDevice( pXDevice, i ); //Msg( "UserID %d: %s\n", i+1, pXDevice->userId != INVALID_USER_ID ? "GamePad" : "???" ); } } bool PS3IsNavController( int userId ) { #if defined( _PS3 ) CellPadPeriphInfo padPeriphInfo; int ret = cellPadPeriphGetInfo( &padPeriphInfo); if ( ret == CELL_PAD_OK && padPeriphInfo.pclass_type[ userId ] == CELL_PAD_PCLASS_TYPE_NAVIGATION ) return true; #endif return false; } //----------------------------------------------------------------------------- // Purpose: Open an Xbox controller //----------------------------------------------------------------------------- void CInputSystem::OpenXDevice( xdevice_t* pXDevice, int userId ) { XINPUT_CAPABILITIES capabilities; // Invalidate device properties pXDevice->userId = INVALID_USER_ID; pXDevice->active = false; DWORD result = XINPUTGETCAPABILITIES( userId, XINPUT_FLAG_GAMEPAD, &capabilities ); if ( result == ERROR_SUCCESS ) { bool bIsSupported = false; if ( IsGameConsole() ) { // TCR says that we cannot restrict input based on subtype, so don't check it bIsSupported = ( capabilities.Type == XINPUT_DEVTYPE_GAMEPAD ); } else { // Current version of XInput mistakenly returns 0 as the Type. Ignore it and ensure the subtype is a gamepad. bIsSupported = ( capabilities.SubType == XINPUT_DEVSUBTYPE_GAMEPAD ); } if ( !bIsSupported ) { // TBD: This may not be sufficient to not crash us later Assert( 0 && "Unsupported XDevice Type" ); return; } #if defined ( _PS3 ) if ( PS3IsNavController( userId ) ) { SetInputDeviceConnected( INPUT_DEVICE_MOVE_NAV_CONTROLLER ); } else #endif { SetInputDeviceConnected( INPUT_DEVICE_GAMEPAD ); } // valid pXDevice->type = capabilities.Type; pXDevice->subtype = capabilities.SubType; pXDevice->flags = capabilities.Flags; pXDevice->userId = userId; pXDevice->active = true; pXDevice->quitTimeout = 0; pXDevice->dpadLock = 0; // left stick, default to narrow zone pXDevice->stickThreshold[STICK1_AXIS_X] = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE; pXDevice->stickScale[STICK1_AXIS_X] = XBX_STICK_SCALE_LEFT( XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE ); pXDevice->stickThreshold[STICK1_AXIS_Y] = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE; pXDevice->stickScale[STICK1_AXIS_Y] = XBX_STICK_SCALE_DOWN( XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE ); // right stick, default to narrow zone pXDevice->stickThreshold[STICK2_AXIS_X] = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; pXDevice->stickScale[STICK2_AXIS_X] = XBX_STICK_SCALE_LEFT( XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE ); pXDevice->stickThreshold[STICK2_AXIS_Y] = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; pXDevice->stickScale[STICK2_AXIS_Y] = XBX_STICK_SCALE_DOWN( XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE ); pXDevice->vibration.wLeftMotorSpeed = WORD( 0 ); pXDevice->vibration.wRightMotorSpeed = WORD( 0 ); pXDevice->pendingRumbleUpdate = false; ++m_nJoystickCount; } } //----------------------------------------------------------------------------- // Purpose: Close an Xbox controller //----------------------------------------------------------------------------- void CInputSystem::CloseXDevice( xdevice_t* pXDevice ) { pXDevice->userId = INVALID_USER_ID; pXDevice->active = false; --m_nJoystickCount; } //----------------------------------------------------------------------------- // Purpose: Sample the console mouse (currently only implemented for PS3). //----------------------------------------------------------------------------- void CInputSystem::PollXMouse() { #ifdef _PS3 if( (!(g_PS3_XInputInfo.m_CellMouseInfo.info & CELL_MOUSE_INFO_INTERCEPTED)) && (g_PS3_XInputInfo.m_CellMouseInfo.status[0] == CELL_MOUSE_STATUS_CONNECTED) && (g_PS3_XInputInfo.m_CellMouseData[0].update == CELL_MOUSE_DATA_UPDATE) && IsDeviceReadingInput( INPUT_DEVICE_KEYBOARD_MOUSE ) ) { int oldMouseX = m_mouseRawAccumX; int oldMouseY = m_mouseRawAccumY; m_mouseRawAccumX += g_PS3_XInputInfo.m_CellMouseData[0].x_axis; m_mouseRawAccumY += g_PS3_XInputInfo.m_CellMouseData[0].y_axis; // handle positional values bool bXChanged = ( m_mouseRawAccumX != oldMouseX ); bool bYChanged = ( m_mouseRawAccumY != oldMouseY ); if ( bXChanged ) { PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_X, m_mouseRawAccumX, 0 ); } if ( bYChanged ) { PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_Y, m_mouseRawAccumY, 0 ); } if ( bXChanged || bYChanged ) { PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_XY, m_mouseRawAccumX, m_mouseRawAccumY ); } // handle buttons if((g_PS3_XInputInfo.m_CellMouseData[0].buttons & PS3_MOUSE_LEFT) && !(g_PS3_XInputInfo.m_CellMouseDataOld[0].buttons & PS3_MOUSE_LEFT)) { PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, MOUSE_LEFT, MOUSE_LEFT ); // [dkorus] check whether we're trying to set the current controller if( m_setCurrentInputDeviceOnNextButtonPress ) { if( IsInputDeviceConnected( INPUT_DEVICE_KEYBOARD_MOUSE ) ) { SetCurrentInputDevice( INPUT_DEVICE_KEYBOARD_MOUSE ); m_setCurrentInputDeviceOnNextButtonPress = false; } } } if((!(g_PS3_XInputInfo.m_CellMouseData[0].buttons & PS3_MOUSE_LEFT)) && (g_PS3_XInputInfo.m_CellMouseDataOld[0].buttons & PS3_MOUSE_LEFT)) { PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, MOUSE_LEFT, MOUSE_LEFT ); } if((g_PS3_XInputInfo.m_CellMouseData[0].buttons & PS3_MOUSE_RIGHT) && !(g_PS3_XInputInfo.m_CellMouseDataOld[0].buttons & PS3_MOUSE_RIGHT)) { PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, MOUSE_RIGHT, MOUSE_RIGHT ); } if((!(g_PS3_XInputInfo.m_CellMouseData[0].buttons & PS3_MOUSE_RIGHT)) && (g_PS3_XInputInfo.m_CellMouseDataOld[0].buttons & PS3_MOUSE_RIGHT)) { PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, MOUSE_RIGHT, MOUSE_RIGHT ); } if((g_PS3_XInputInfo.m_CellMouseData[0].buttons & PS3_MOUSE_WHEEL) && !(g_PS3_XInputInfo.m_CellMouseDataOld[0].buttons & PS3_MOUSE_WHEEL)) { PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, MOUSE_MIDDLE, MOUSE_MIDDLE ); } if((!(g_PS3_XInputInfo.m_CellMouseData[0].buttons & PS3_MOUSE_WHEEL)) && (g_PS3_XInputInfo.m_CellMouseDataOld[0].buttons & PS3_MOUSE_WHEEL)) { PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, MOUSE_MIDDLE, MOUSE_MIDDLE ); } if(g_PS3_XInputInfo.m_CellMouseData[0].wheel > 0) { PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, MOUSE_WHEEL_UP, MOUSE_WHEEL_UP); PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, MOUSE_WHEEL_UP, MOUSE_WHEEL_UP); } else if(g_PS3_XInputInfo.m_CellMouseData[0].wheel < 0) { PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, MOUSE_WHEEL_DOWN, MOUSE_WHEEL_DOWN); PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, MOUSE_WHEEL_DOWN, MOUSE_WHEEL_DOWN); } } #endif } //----------------------------------------------------------------------------- // Purpose: Sample the console keyboard (currently only implemented for PS3). //----------------------------------------------------------------------------- void CInputSystem::PollXKeyboard() { #ifdef _PS3 if( (!(g_PS3_XInputInfo.m_CellKbInfo.info & CELL_KB_INFO_INTERCEPTED)) && (g_PS3_XInputInfo.m_CellKbInfo.status[0] == CELL_KB_STATUS_CONNECTED) && (g_PS3_XInputInfo.m_CellKbData[0].len > 0) && IsDeviceReadingInput( INPUT_DEVICE_KEYBOARD_MOUSE ) ) { CBitVec<256> keysPressed; keysPressed.Init(0); // Check for key presses for(int i=0; istates[pXDevice->newState] ); if ( result == ERROR_SUCCESS ) { ReleaseAllButtons( JOYSTICK_BUTTON_INTERNAL( pXDevice->userId, JOYSTICK_FIRST ), JOYSTICK_BUTTON_INTERNAL( pXDevice->userId + 1, JOYSTICK_FIRST ) - 1 ); ZeroAnalogState( JOYSTICK_AXIS_INTERNAL( pXDevice->userId, JOYSTICK_FIRST_AXIS ), JOYSTICK_AXIS_INTERNAL( pXDevice->userId, JOYSTICK_FIRST_AXIS ) - 1 ); memset( &m_appXKeys[pXDevice->userId][0], 0, XK_MAX_KEYS * sizeof(appKey_t) ); } } } m_flLastControllerPollTime = -1; return; } else if ( m_flLastControllerPollTime != -1 && m_flLastControllerPollTime + flCheckDelay <= Plat_FloatTime() ) { bool bAnyActive = false; #ifdef PLATFORM_PS3 if ( g_pMoveController->m_bEnabled && !ps3_move_enabled.GetBool() ) { g_pMoveController->Disable(); } else if ( !g_pMoveController->m_bEnabled && ps3_move_enabled.GetBool() ) { g_pMoveController->Enable(); } PS3_XInputPollEverything( m_pPS3CellPadDataHook, m_pPS3CellNoPadDataHook ); // update all the joysticks // Read move controller data if( g_pMoveController->m_bEnabled && (g_PS3_XInputInfo.m_moveControllerState.m_CellGemInfo.status[0]==CELL_GEM_STATUS_READY) && (g_PS3_XInputInfo.m_moveControllerState.m_aStatus[0] == CELL_OK) ) { m_bMotionControllerActive = true; bool bMcVisible = g_PS3_XInputInfo.m_moveControllerState.m_aCellGemState[0].tracking_flags & CELL_GEM_TRACKING_FLAG_VISIBLE; if (bMcVisible) { m_qMotionControllerOrientation = Quaternion( -g_PS3_XInputInfo.m_moveControllerState.m_aCellGemState[0].quat[2], -g_PS3_XInputInfo.m_moveControllerState.m_aCellGemState[0].quat[0], g_PS3_XInputInfo.m_moveControllerState.m_aCellGemState[0].quat[1], g_PS3_XInputInfo.m_moveControllerState.m_aCellGemState[0].quat[3]); //m_vecMotionControllerAngle = g_PS3_XInputInfo.m_moveControllerState.m_aAngle[0]; m_vecMotionControllerPos = g_PS3_XInputInfo.m_moveControllerState.m_pos[0]; m_fMotionControllerPosX = g_PS3_XInputInfo.m_moveControllerState.m_posX[0]; m_fMotionControllerPosY = g_PS3_XInputInfo.m_moveControllerState.m_posY[0]; } else { // If not visible, set screen x and y position to be 0.0 m_fMotionControllerPosX = m_fMotionControllerPosX*0.85f; m_fMotionControllerPosY = m_fMotionControllerPosY*0.85f; } } else { m_bMotionControllerActive = false; } m_nMotionControllerStatusFlags = g_PS3_XInputInfo.m_moveControllerState.m_aStatusFlags[0]; #endif PollXMouse(); PollXKeyboard(); xdevice_t* pXDevice = m_XDevices; for ( int userId = 0; userId < XUSER_MAX_COUNT; ++userId, ++pXDevice ) { // Get input data in the form of Xbox controller data (for PS3, map onto Xbox struct) DWORD result = XINPUTGETSTATE( userId, &pXDevice->states[pXDevice->newState] ); switch ( result ) { case ERROR_SUCCESS: if ( !pXDevice->active ) { // just inserted OpenXDevice( pXDevice, userId ); int openedPort = userId; #ifdef _PS3 if ( ( XBX_GetNumGameUsers() <= 1 ) && !ps3_joy_ss.GetBool() ) openedPort = 0; #endif PostEvent( IE_ControllerInserted, m_nLastSampleTick, openedPort ); #if defined( _PS3 ) if ( PS3IsNavController( userId ) ) { SetInputDeviceConnected( INPUT_DEVICE_MOVE_NAV_CONTROLLER, true ); } else #endif { SetInputDeviceConnected( INPUT_DEVICE_GAMEPAD, true ); ConVarRef var( "joystick" ); if ( var.IsValid() && var.GetBool() == false ) var.SetValue( 1 ); } } bAnyActive = true; // See if primary user has been set, and if so then block all other input // if ( pXDevice->userId == m_PrimaryUserId || m_PrimaryUserId == INVALID_USER_ID ) { ReadXDevice( pXDevice ); WriteToXDevice( pXDevice ); } break; case ERROR_DEVICE_NOT_CONNECTED: if ( pXDevice->active ) { // just removed int closedPort = pXDevice->userId; CloseXDevice( pXDevice ); #ifdef _X360 if ( XBX_GetSlotByUserId( closedPort ) >= 0 ) #else if ( 1 ) #endif { // Controller unplugged - game needs to take action // Release buttons of the specific joystick that was unplugged ReleaseAllButtons( JOYSTICK_BUTTON_INTERNAL( closedPort, JOYSTICK_FIRST ), JOYSTICK_BUTTON_INTERNAL( closedPort + 1, JOYSTICK_FIRST ) - 1 ); ZeroAnalogState( JOYSTICK_AXIS_INTERNAL( closedPort, JOYSTICK_FIRST_AXIS ), JOYSTICK_AXIS_INTERNAL( closedPort, JOYSTICK_FIRST_AXIS ) - 1 ); memset( &m_appXKeys[closedPort][0], 0, XK_MAX_KEYS * sizeof(appKey_t) ); #ifdef _PS3 if ( ( XBX_GetNumGameUsers() <= 1 ) && !ps3_joy_ss.GetBool() && ( closedPort + 1 == g_PS3_XInputInfo.m_iActiveSingleControllerIndex ) ) { closedPort = 0; PostEvent( IE_ControllerUnplugged, m_nLastSampleTick, closedPort ); } #else PostEvent( IE_ControllerUnplugged, m_nLastSampleTick, closedPort ); #endif #if defined( _PS3 ) if ( PS3IsNavController( userId ) ) { SetInputDeviceConnected( INPUT_DEVICE_MOVE_NAV_CONTROLLER, false ); } else #endif { SetInputDeviceConnected( INPUT_DEVICE_GAMEPAD, false ); } } } break; } } // We don't need to check every frame if nothing is connected if ( bAnyActive ) { m_bXController = true; ConVarRef var( "joy_xcontroller_found" ); if ( var.IsValid() && var.GetBool() == false ) var.SetValue( 1 ); } else { m_bXController = false; } m_flLastControllerPollTime = Plat_FloatTime(); } } #ifdef _PS3 static float g_ps3_flTimeStartButtonIdentificationMode = 0.0f; void CInputSystem::SetPS3StartButtonIdentificationMode() { g_ps3_flTimeStartButtonIdentificationMode = Plat_FloatTime(); } #endif //----------------------------------------------------------------------------- // Purpose: Post Xbox events, ignoring key repeats //----------------------------------------------------------------------------- void CInputSystem::PostXKeyEvent( int userId, xKey_t xKey, int nSample ) { AnalogCode_t code = ANALOG_CODE_LAST; float value = 0.f; // Map the physical controller slot to the split screen slot #if defined( _GAMECONSOLE ) int nMsgSlot = XBX_GetSlotByUserId( userId ); #ifdef _PS3 if ( ( XBX_GetNumGameUsers() <= 1 ) && !ps3_joy_ss.GetBool() ) { // In PS3 START button identification mode START key notification // is replaced with INACTIVE_START notification that can identify // controller that pressed the button if ( ( xKey == XK_BUTTON_START ) && ( nMsgSlot < 0 ) && ( ( Plat_FloatTime() - g_ps3_flTimeStartButtonIdentificationMode ) < 0.5f ) ) { xKey = XK_BUTTON_INACTIVE_START; nMsgSlot = userId; } else { // When we don't have splitscreen then any controller can // play and will be visible as controller #0 nMsgSlot = 0; } } #endif if ( nMsgSlot < 0 ) { // special case, that if you press start on a controller we've marked inactive, switch it to an // XK_BUTTON_INACTIVE_START which you can handle joins from inactive controllers if ( xKey == XK_BUTTON_START ) { xKey = XK_BUTTON_INACTIVE_START; nMsgSlot = userId; } else { return; // We are not listening to this controller (not signed in and assigned) } } #else //defined( _GAMECONSOLE ) int nMsgSlot = userId; #endif //defined( _GAMECONSOLE ) int nSampleThreshold = 0; // Look for changes on the analog axes switch( xKey ) { case XK_STICK1_LEFT: case XK_STICK1_RIGHT: { code = (AnalogCode_t)JOYSTICK_AXIS( nMsgSlot, JOY_AXIS_X ); value = ( xKey == XK_STICK1_LEFT ) ? -nSample : nSample; nSampleThreshold = ( int )( JOYSTICK_ANALOG_BUTTON_THRESHOLD ); } break; case XK_STICK1_UP: case XK_STICK1_DOWN: { code = (AnalogCode_t)JOYSTICK_AXIS( nMsgSlot, JOY_AXIS_Y ); value = ( xKey == XK_STICK1_UP ) ? -nSample : nSample; nSampleThreshold = ( int )( JOYSTICK_ANALOG_BUTTON_THRESHOLD ); } break; case XK_STICK2_LEFT: case XK_STICK2_RIGHT: { code = (AnalogCode_t)JOYSTICK_AXIS( nMsgSlot, JOY_AXIS_U ); value = ( xKey == XK_STICK2_LEFT ) ? -nSample : nSample; nSampleThreshold = ( int )( JOYSTICK_ANALOG_BUTTON_THRESHOLD ); } break; case XK_STICK2_UP: case XK_STICK2_DOWN: { code = (AnalogCode_t)JOYSTICK_AXIS( nMsgSlot, JOY_AXIS_R ); value = ( xKey == XK_STICK2_UP ) ? -nSample : nSample; nSampleThreshold = ( int )( JOYSTICK_ANALOG_BUTTON_THRESHOLD ); } break; } // Store the analog event if ( ANALOG_CODE_LAST != code ) { InputState_t &state = m_InputState[ m_bIsPolling ]; state.m_pAnalogDelta[ code ] = ( int )( value - state.m_pAnalogValue[ code ] ); state.m_pAnalogValue[ code ] = ( int )value; if ( state.m_pAnalogDelta[ code ] != 0 ) { PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, code, ( int )value, state.m_pAnalogDelta[ code ] ); } } // store the key m_appXKeys[userId][xKey].sample = nSample; if ( nSample > nSampleThreshold ) { m_appXKeys[userId][xKey].repeats++; } else { m_appXKeys[userId][xKey].repeats = 0; nSample = 0; } if ( m_appXKeys[userId][xKey].repeats > 1 ) { // application cannot handle streaming keys // first keypress is the only edge trigger return; } // package the key ButtonCode_t buttonCode = XKeyToButtonCode( nMsgSlot, xKey ); if ( nSample ) { PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, buttonCode, buttonCode ); // [dkorus] check whether we're trying to set the current controller if( ( buttonCode == KEY_XBUTTON_A || buttonCode == XK_BUTTON_START ) && m_setCurrentInputDeviceOnNextButtonPress ) { #if defined( _PS3 ) if ( PS3IsNavController( userId ) ) { if ( IsInputDeviceConnected( INPUT_DEVICE_MOVE_NAV_CONTROLLER ) ) { // [dkorus] if someone trys to lock input with the nav controller... // select the sharpshooter if it's available // otherwise select the MOVE if it's available if ( IsInputDeviceConnected( INPUT_DEVICE_SHARPSHOOTER ) ) { SetCurrentInputDevice( INPUT_DEVICE_SHARPSHOOTER ); m_setCurrentInputDeviceOnNextButtonPress = false; } else if ( IsInputDeviceConnected( INPUT_DEVICE_PLAYSTATION_MOVE ) ) { SetCurrentInputDevice( INPUT_DEVICE_PLAYSTATION_MOVE ); m_setCurrentInputDeviceOnNextButtonPress = false; } } } else #endif { if ( IsInputDeviceConnected( INPUT_DEVICE_GAMEPAD ) ) { SetCurrentInputDevice( INPUT_DEVICE_GAMEPAD ); ConVarRef var( "joystick" ); if( var.IsValid( ) ) var.SetValue( 1 ); m_setCurrentInputDeviceOnNextButtonPress = false; } } } } else { PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, buttonCode, buttonCode ); } } //----------------------------------------------------------------------------- // Purpose: Send force feedback to an Xbox controller //----------------------------------------------------------------------------- void CInputSystem::WriteToXDevice( xdevice_t* pXDevice ) { if ( pXDevice->pendingRumbleUpdate ) { XINPUTSETSTATE( pXDevice->userId, &pXDevice->vibration ); pXDevice->pendingRumbleUpdate = false; } } static int NormalizeStickValue_Cross( int nValue, int nThreshold, float flScale ) { if ( nValue >= -nThreshold ) { if ( nValue <= nThreshold ) { return 0; } else { return ( int )( ( nValue - nThreshold ) * flScale ); } } else { return ( int )( ( nValue + nThreshold ) * flScale ); } } static void NormalizeStickValue_Square( int nX, int nY, int nThresholdX, int nThresholdY, int *pX, int *pY ) { if ( nX >= -nThresholdX && nX <= nThresholdX && nY >= -nThresholdY && nY <= nThresholdY ) { *pX = 0; *pY = 0; } else { *pX = nX; *pY = nY; } } void CInputSystem::HandleXDeviceAxis( xdevice_t *pXDevice, int nAxisValue, xKey_t negativeKey, xKey_t positiveKey, int axisID ) { xKey_t key; // Queue stick axis push response if ( nAxisValue < 0 ) { PostXKeyEvent( pXDevice->userId, negativeKey, -nAxisValue ); key = negativeKey; } else if ( nAxisValue > 0 ) { PostXKeyEvent( pXDevice->userId, positiveKey, nAxisValue ); key = positiveKey; } else { key = XK_NULL; } if ( pXDevice->lastStickKeys[axisID] && pXDevice->lastStickKeys[axisID] != key ) { // Queue stick axis release response PostXKeyEvent( pXDevice->userId, pXDevice->lastStickKeys[axisID], 0 ); } pXDevice->lastStickKeys[axisID] = key; } //----------------------------------------------------------------------------- // Purpose: Queue input key events for a device //----------------------------------------------------------------------------- void CInputSystem::ReadXDevice( xdevice_t* pXDevice ) { XINPUT_STATE* oldStatePtr; XINPUT_STATE* newStatePtr; int mask; int buttons; int sample; oldStatePtr = &pXDevice->states[pXDevice->newState ^ 1]; newStatePtr = &pXDevice->states[pXDevice->newState]; // forceful exit if ( newStatePtr->Gamepad.wButtons == ( XINPUT_GAMEPAD_START|XINPUT_GAMEPAD_BACK ) ) { if ( !pXDevice->quitTimeout ) { pXDevice->quitTimeout = GetTickCount(); } else if ( GetTickCount() - pXDevice->quitTimeout > 2*1000 ) { // mandatory 2sec hold pXDevice->quitTimeout = 0; ProcessEvent( WM_XREMOTECOMMAND, 0, (LPARAM)"quit_gameconsole" ); } } else { // reset pXDevice->quitTimeout = 0; } // No changes if packet numbers match if (( oldStatePtr->dwPacketNumber == newStatePtr->dwPacketNumber ) && !m_bMotionControllerActive) return; // PS3 builds allow gamepad input even if we're using another device // move and other controllers use the same gamepad button presses #if !defined ( _PS3 ) if ( !IsDeviceReadingInput( INPUT_DEVICE_GAMEPAD ) ) return; #endif // digital events buttons = newStatePtr->Gamepad.wButtons ^ oldStatePtr->Gamepad.wButtons; if ( buttons ) { // determine if dpad press is axial or diagonal combos bool bDpadIsAxial = IsPowerOfTwo( newStatePtr->Gamepad.wButtons & (XINPUT_GAMEPAD_DPAD_UP|XINPUT_GAMEPAD_DPAD_DOWN|XINPUT_GAMEPAD_DPAD_LEFT|XINPUT_GAMEPAD_DPAD_RIGHT) ); // determine digital difference - up or down? for ( int i = 0; i < sizeof( g_digitalXKeyTable )/sizeof( g_digitalXKeyTable[0] ); ++i ) { mask = buttons & g_digitalXKeyTable[i].xinput; if ( !mask ) continue; if ( mask & newStatePtr->Gamepad.wButtons ) { // down event sample = XBX_MAX_BUTTONSAMPLE; } else { // up event sample = 0; } // due to rocker mechanics, allow only 1 of 4 behavior // the last down axial event trumps, preventing diagonals causing multiple events if ( mask & (XINPUT_GAMEPAD_DPAD_UP|XINPUT_GAMEPAD_DPAD_DOWN|XINPUT_GAMEPAD_DPAD_LEFT|XINPUT_GAMEPAD_DPAD_RIGHT) ) { if ( !bDpadIsAxial ) { // diagonal dpad event, discard any dpad event that isn't the lock if ( pXDevice->dpadLock && pXDevice->dpadLock != mask ) continue; } else if ( sample ) { // axial dpad down event, set the lock pXDevice->dpadLock = mask; } } PostXKeyEvent( pXDevice->userId, (xKey_t)g_digitalXKeyTable[i].xkey, sample ); } } // analog events // queue left trigger axis analog response if ( newStatePtr->Gamepad.bLeftTrigger <= XINPUT_GAMEPAD_TRIGGER_THRESHOLD ) { newStatePtr->Gamepad.bLeftTrigger = 0; } sample = newStatePtr->Gamepad.bLeftTrigger; if ( sample != oldStatePtr->Gamepad.bLeftTrigger ) { PostXKeyEvent( pXDevice->userId, XK_BUTTON_LTRIGGER, ( int )( sample * ( float )XBX_MAX_BUTTONSAMPLE/( float )XBX_MAX_ANALOGSAMPLE ) ); } // queue right trigger axis analog response if ( newStatePtr->Gamepad.bRightTrigger <= XINPUT_GAMEPAD_TRIGGER_THRESHOLD ) { newStatePtr->Gamepad.bRightTrigger = 0; } sample = newStatePtr->Gamepad.bRightTrigger; if ( sample != oldStatePtr->Gamepad.bRightTrigger ) { PostXKeyEvent( pXDevice->userId, XK_BUTTON_RTRIGGER, ( int )( sample * ( float )XBX_MAX_BUTTONSAMPLE/( float )XBX_MAX_ANALOGSAMPLE ) ); } int nX1 = 0, nY1 = 0, nX2 = 0, nY2 = 0; extern ConVar joy_deadzone_mode; switch ( joy_deadzone_mode.GetInt() ) { case JOYSTICK_DEADZONE_CROSS: nX1 = NormalizeStickValue_Cross( newStatePtr->Gamepad.sThumbLX, pXDevice->stickThreshold[STICK1_AXIS_X], pXDevice->stickScale[STICK1_AXIS_X] ); nY1 = NormalizeStickValue_Cross( newStatePtr->Gamepad.sThumbLY, pXDevice->stickThreshold[STICK1_AXIS_Y], pXDevice->stickScale[STICK1_AXIS_Y] ); nX2 = NormalizeStickValue_Cross( newStatePtr->Gamepad.sThumbRX, pXDevice->stickThreshold[STICK2_AXIS_X], pXDevice->stickScale[STICK2_AXIS_X] ); nY2 = NormalizeStickValue_Cross( newStatePtr->Gamepad.sThumbRY, pXDevice->stickThreshold[STICK2_AXIS_Y], pXDevice->stickScale[STICK2_AXIS_Y] ); break; case JOYSTICK_DEADZONE_SQUARE: NormalizeStickValue_Square( newStatePtr->Gamepad.sThumbLX, newStatePtr->Gamepad.sThumbLY, pXDevice->stickThreshold[STICK1_AXIS_X], pXDevice->stickThreshold[STICK1_AXIS_Y], &nX1, &nY1 ); NormalizeStickValue_Square( newStatePtr->Gamepad.sThumbRX, newStatePtr->Gamepad.sThumbRY, pXDevice->stickThreshold[STICK2_AXIS_X], pXDevice->stickThreshold[STICK2_AXIS_Y], &nX2, &nY2 ); break; default: UNREACHABLE(); } HandleXDeviceAxis( pXDevice, nX1, XK_STICK1_LEFT, XK_STICK1_RIGHT, STICK1_AXIS_X ); HandleXDeviceAxis( pXDevice, nY1, XK_STICK1_DOWN, XK_STICK1_UP, STICK1_AXIS_Y ); HandleXDeviceAxis( pXDevice, nX2, XK_STICK2_LEFT, XK_STICK2_RIGHT, STICK2_AXIS_X ); HandleXDeviceAxis( pXDevice, nY2, XK_STICK2_DOWN, XK_STICK2_UP, STICK2_AXIS_Y ); // toggle the states pXDevice->newState ^= 1; } void CInputSystem::QueueMoveControllerRumble( float fRightMotor ) { #if defined( _PS3 ) WORD wNewRight = ( WORD )( XBX_MAX_MOTOR_SPEED * fRightMotor ); if ( wNewRight > XBX_MAX_MOTOR_SPEED ) wNewRight = XBX_MAX_MOTOR_SPEED; if ( fRightMotor != g_PS3_XInputInfo.ps3_move_rumble_value ) { g_PS3_XInputInfo.ps3_move_rumble_value = wNewRight; g_PS3_XInputInfo.ps3_move_rumble_queued = true; } #endif } //----------------------------------------------------------------------------- // Purpose: Queues a left and right motor value for the Xbox controller //----------------------------------------------------------------------------- void CInputSystem::SetXDeviceRumble( float fLeftMotor, float fRightMotor, int userId ) { WORD wOldLeft, wOldRight;// Last values we sent WORD wNewLeft, wNewRight;// Values we're about to send. xdevice_t* pXDevice; if ( IsDeviceReadingInput( INPUT_DEVICE_PLAYSTATION_MOVE ) || IsDeviceReadingInput( INPUT_DEVICE_SHARPSHOOTER ) ) { // [dkorus] route move controller commands seperately, no need for an pXDevice for the move controller // note: move controller only needs one motor setting QueueMoveControllerRumble( fRightMotor ); return; } if ( userId == INVALID_USER_ID ) return; pXDevice = &m_XDevices[userId]; // can only set rumble on active controllers if ( pXDevice->userId == INVALID_USER_ID ) { return; } wNewLeft = ( WORD )( XBX_MAX_MOTOR_SPEED * fLeftMotor ); wNewRight = ( WORD )( XBX_MAX_MOTOR_SPEED * fRightMotor ); if ( wNewLeft > XBX_MAX_MOTOR_SPEED ) wNewLeft = XBX_MAX_MOTOR_SPEED; if ( wNewRight > XBX_MAX_MOTOR_SPEED ) wNewRight = XBX_MAX_MOTOR_SPEED; wOldLeft = pXDevice->vibration.wLeftMotorSpeed; wOldRight = pXDevice->vibration.wRightMotorSpeed; if ( wNewLeft != wOldLeft || wNewRight != wOldRight ) { pXDevice->vibration.wLeftMotorSpeed = wNewLeft; pXDevice->vibration.wRightMotorSpeed = wNewRight; pXDevice->pendingRumbleUpdate = true; } } void CInputSystem::SetMotionControllerCalibrationInvalid( void ) { #if defined( PLATFORM_PS3 ) g_pMoveController->InvalidateCalibration(); #endif // PLATFORM_PS3 } void CInputSystem::StepMotionControllerCalibration( void ) { #if defined( PLATFORM_PS3 ) g_pMoveController->StepMotionControllerCalibration(); #endif // PLATFORM_PS3 } void CInputSystem::ResetMotionControllerScreenCalibration( void ) { #if defined( PLATFORM_PS3 ) g_pMoveController->ResetMotionControllerScreenCalibration(); #endif // PLATFORM_PS3 }