// // name: controlmgr.cpp // description: class that encapsulates all game controllers (pad, mouse, keyboard) // Rage headers #include "bank/io.h" #include "bank/bkmgr.h" #include "bank/bank.h" #include "grcore/device.h" #include "input/headtracking.h" #include "profile/telemetry.h" #include "security/yubikey/yubikeymanager.h" #include "system/exception.h" #include "system/ipc.h" #include "system/param.h" #include "system/userlist.h" // game headers #include "core/game.h" #include "SaveLoad/savegame_autoload.h" #include "script/script_debug.h" // for CScriptDebug::GetDisableDebugCamAndPlayerWarping() #include "system/controlmgr.h" #include "debug/debug.h" #include "Frontend/NewHud.h" #include "Frontend/PauseMenu.h" #include "Frontend/WarningScreen.h" #include "Peds/Ped.h" #include "scene/playerswitch/PlayerSwitchInterface.h" #include "scene/world/gameWorld.h" #include "script/script_debug.h" #include "text/messages.h" #include "Network/live/livemanager.h" #include "Network/NetworkInterface.h" #include "camera/CamInterface.h" #include "camera/viewports/Viewport.h" #include "camera/viewports/ViewportManager.h" #include "Network/NetworkInterface.h" #include "system/system.h" #include "control/replay/replay.h" #include "control/videorecording/videorecording.h" #include "replaycoordinator/ReplayCoordinator.h" #include "text/TextFormat.h" #if RSG_PC #include "frontend/TextInputBox.h" #include "rline/rlpc.h" #endif // RSG_PC #if RSG_DURANGO #include "rline/durango/rlxbl_interface.h" #endif #if __BANK #include "pathserver/PathServer.h" #include "Cutscene/CutSceneManagerNew.h" #endif // static member variables sysIpcThreadId CControlMgr::m_updateThreadId; rage::sysIpcCurrentThreadId CControlMgr::sm_ThreadId = sysIpcCurrentThreadIdInvalid; sysIpcSema CControlMgr::m_runUpdateSema; sysIpcSema CControlMgr::m_updateFinishedSema; sysCriticalSectionToken CControlMgr::m_Cs; CPad CControlMgr::m_pads[NUM_PADS]; CPad CControlMgr::m_debugPad; CKeyboard CControlMgr::m_keyboard; CControl CControlMgr::m_controls[MAX_CONTROL]; //u32 CControlMgr::ms_disablePlayerControls; moved to CPlayerInfo //eCONTROL_METHOD CControlMgr::m_ControlMethod; s32 CControlMgr::sm_PlayerPadIndex = -1; s32 CControlMgr::sm_DebugPadIndex = -1; bool CControlMgr::sm_bNoPadMessageActive = false; bool volatile CControlMgr::sm_bShutdownThreads = false; bank_u32 CControlMgr::sm_InputDisableDuration = 500U; #if DEBUG_PAD_INPUT PARAM(showDebugPadValues, "Shows the debug pad values."); bool CControlMgr::sm_bDebugPadValues = false; #endif #if __BANK s32 CControlMgr::sm_nSaveAsDefaultNum = CONTROLS_CONFIG_DEFAULT; u32 CControlMgr::sm_iShakeDuration0 = 0; u32 CControlMgr::sm_iShakeDuration1 = 0; u32 CControlMgr::sm_iShakeFrequency0 = 0; u32 CControlMgr::sm_iShakeFrequency1 = 0; s32 CControlMgr::sm_iShakeDelayAfterThisOne = 0; float CControlMgr::sm_fMultiplier = 0.0f; bool CControlMgr::sm_bOverrideShakeFreq0 = false; bool CControlMgr::sm_bOverrideShakeFreq1 = false; bool CControlMgr::sm_bOverrideShakeDur0 = false; bool CControlMgr::sm_bOverrideShakeDur1 = false; bool CControlMgr::sm_bOverrideShakeDelay = false; bool CControlMgr::sm_bUseThisMultiplier = false; u32 CControlMgr::sm_iDebugShakeDuration = 500; u32 CControlMgr::sm_iDebugMotorToShake = 0; bool CControlMgr::sm_bDebugShakeMotor = false; float CControlMgr::sm_fDebugShakeIntensity = 0.5f; s32 CControlMgr::sm_iHardcodedMappingIndex = 0; char CControlMgr::sm_CustomInputMetaFilePath[RAGE_MAX_PATH]; char CControlMgr::sm_DefaultVirtualKeyboardText[MAX_KEYBOARD_TEXT] = "Default Text"; int CControlMgr::sm_VirtualKeyboardTextLimit = ioVirtualKeyboard::MAX_STRING_LENGTH_FOR_VIRTUAL_KEYBOARD; s32 CControlMgr::sm_KeyboardMode = 0; bool CControlMgr::sm_bCheckKeyboard = false; #endif //__BANK // static variables bool CControlMgr::sm_bDebugPad = false; Functor0 CControlMgr::sm_initProfileFn = MakeFunctor(Functor0::NullFn); Functor0 CControlMgr::sm_shutdownProfileFn = MakeFunctor(Functor0::NullFn); bool CControlMgr::sm_bShakeWhenControllerDisabled = false; #if RSG_DURANGO sysCriticalSectionToken CControlMgr::sm_ConstrainedCs; ServiceDelegate CControlMgr::sm_Delegate; bool CControlMgr::sm_IsGameConstrained = false; #endif // RSG_DURANGO #if DEBUG_DRAW PARAM(debugInputs, "Shows input values and disable state."); CControlMgr::InputDisplayType CControlMgr::sm_eInputDisplayType = CControlMgr::IDT_NONE; #if __BANK bool CControlMgr::sm_bPrintToGameplayLog = false; const char* CControlMgr::sm_InputDislayTypeNames[IDT_AMOUNT] = { "Do not show", "Show all inputs", "Show non-zero inputs", "Show disabled inputs" }; #endif // __BANK float CControlMgr::sm_InputDisplayScale = 1.0f; float CControlMgr::sm_InputDisplayX = 0.05f; s32 CControlMgr::sm_InputDislayMaxLength = 0; #endif // DEBUG_DRAW #if !__FINAL bool CControlMgr::sm_bAllowTildeCharacterFromVirtualKeyboard = false; #endif // !__FINAL s32 CControlMgr::sm_fontBitField = 0; s32 gPadNumberToDebug = 0; bool gbShowDebugKeys = false; bool gbShowLevelDebugKeys = false; bool gSceneRenderingPaused = false; ioVirtualKeyboard CControlMgr::sm_VirtualKeyboard; char CControlMgr::sm_VirtualKeyboardResult[ioVirtualKeyboard::VIRTUAL_KEYBOARD_RESULT_BUFFER_SIZE]; bool CControlMgr::sm_bVirtualKeyboardIsEnteringAPassword = false; #if OVERLAY_KEYBOARD bool CControlMgr::sm_bUsingOverlayKeyboard = false; #endif // OVERLAY_KEYBOARD NOSTRIP_PC_PARAM(mouseexclusive, "Game uses mouse exclusively."); NOSTRIP_PC_XPARAM(streamingbuild); #if !__FINAL PARAM(usecontroller, "Forces a specific controler to be used."); PARAM(disallowdebugpad, "Don't assume the next pad that has input is the debug pad."); #endif //__FINAL #define TIMESTEP_BETWEEN_DEVICE_CHECK 1000 CPlayerInfo* GetLocalPlayerInfo() { CPed* pPlayer = CGameWorld::FindLocalPlayerSafe(); return (pPlayer ? pPlayer->GetPlayerInfo() : NULL); } // some functions to save and load defaults for debug: #if __BANK // // name: SaveAsDefaultsButton // description: saves the defaults from the bank button using the current default number void CControlMgr::SaveAsDefaultsButton() { char cDefaultsFilename[255]; sprintf(cDefaultsFilename, "%s%d", STANDARD_DEFAULT_CONTROLS_FILENAME, CControlMgr::sm_nSaveAsDefaultNum); GetControl(CONTROL_MAIN_PLAYER).SaveControls(cDefaultsFilename, CONTROLS_VERSION_NUMBER); } // // name: LoadDefaultsButton // description: loads the defaults from the bank button using the current default number void CControlMgr::LoadDefaultsButton() { char cDefaultsFilename[255]; sprintf(cDefaultsFilename, "%s%d", STANDARD_DEFAULT_CONTROLS_FILENAME, CControlMgr::sm_nSaveAsDefaultNum); GetControl(CONTROL_MAIN_PLAYER).LoadControls(cDefaultsFilename, CONTROLS_VERSION_NUMBER); // load the controls GetControl(CONTROL_DEBUG).LoadControls(cDefaultsFilename, CONTROLS_VERSION_NUMBER); // load the controls } #endif //__BANK // // name: Init // description: Initialise class void CControlMgr::Init(unsigned initMode) { if(initMode == INIT_CORE) { // Init Rage classes bool inWindow = CSystem::InWindow(); ::rage::INPUT.Begin(inWindow, PARAM_mouseexclusive.Get()); sm_PlayerPadIndex = -1; sm_bShutdownThreads = false; OVERLAY_KEYBOARD_ONLY(sm_bUsingOverlayKeyboard = false); for(s32 i=0; i(i)); s32 len = static_cast(strlen(name)); // We are ignoring the INPUT_. We don't assume it starts with this. if(strncmp(name, "INPUT_", 6) == 0) { len -= 6; } // add space for the value (-nn.nn) pus a space at the end. len += 8; if(len > sm_InputDislayMaxLength) { sm_InputDislayMaxLength = len; } } s32 displayType = CControlMgr::IDT_NONE; if(PARAM_debugInputs.Get(displayType)) { if(displayType >= 0 && displayType < CControlMgr::IDT_AMOUNT) { sm_eInputDisplayType = static_cast(displayType); } } #endif // DEBUG_DRAW m_runUpdateSema = sysIpcCreateSema(0); m_updateFinishedSema = sysIpcCreateSema(0); m_updateThreadId = sysIpcCreateThread(&*CControlMgr::UpdateThread, NULL, 32*1024, PRIO_HIGHEST, "[GTA] ControlMgr Update Thread", 2, "ControlMgrUpdateThread"); #if RSG_DURANGO sm_Delegate.Bind(&CControlMgr::HandleConstrainedChanged); g_SysService.AddDelegate(&sm_Delegate); #endif // RSG_DURANGO #if RAGE_SEC_ENABLE_YUBIKEY CYubiKeyManager::Create(); #endif } else if(initMode == INIT_SESSION) { // ensure controller 0 is used if it hasn't been set if(GetMainPlayerIndex() == -1) SetMainPlayerIndex(0); // Make sure to clear any old input data history. GetMainPlayerControl().InitEmptyControls(); } for(u32 i = 0; i < MAX_CONTROL; ++i) { m_controls[i].Init(initMode); } } void CControlMgr::ReInitControls() { m_controls[CONTROL_MAIN_PLAYER].SetInitialDefaultMappings(false); #if KEYBOARD_MOUSE_SUPPORT #if __BANK // in bank mode we can disable pc input to better work with rag. if(!PARAM_noKeyboardMouseControls.Get()) #endif // __BANK { m_controls[CONTROL_MAIN_PLAYER].ResetKeyboardMouseSettings(); } #endif m_controls[CONTROL_EMPTY].ClearInputMappings(); #if ENABLE_DEBUG_CONTROLS m_controls[CONTROL_DEBUG].ClearInputMappings(); #endif //ENABLE_DEBUG_CONTROLS } void CControlMgr::SetDefaultLookInversions() { m_controls[CONTROL_MAIN_PLAYER].SetDefaultLookInversions(); m_controls[CONTROL_EMPTY].SetDefaultLookInversions(); #if ENABLE_DEBUG_CONTROLS m_controls[CONTROL_DEBUG].SetDefaultLookInversions(); #endif //ENABLE_DEBUG_CONTROLS } #if __BANK void CControlMgr::LoadCustomControls() { m_controls[CONTROL_MAIN_PLAYER].SetInitialDefaultMappings(false, false, sm_CustomInputMetaFilePath); m_controls[CONTROL_EMPTY].ClearInputMappings(); #if ENABLE_DEBUG_CONTROLS m_controls[CONTROL_DEBUG].ClearInputMappings(); #endif //ENABLE_DEBUG_CONTROLS } #endif // __BANK // // name: Shutdown // description: Shutdown class void CControlMgr::Shutdown(unsigned shutdownMode) { if(shutdownMode == SHUTDOWN_CORE) { #if RSG_DURANGO g_SysService.RemoveDelegate(&sm_Delegate); sm_Delegate.Reset(); #endif // RSG_DURANGO // Shutdown Rage classes ::rage::INPUT.End(); m_keyboard.Shutdown(); sm_bShutdownThreads = true; // Wake up the control update thread to exit. sysIpcSignalSema(m_runUpdateSema); } } void CControlMgr::StartUpdateThread() { PF_AUTO_PUSH_TIMEBAR("CControlMgr::StartUpdateThread"); sysIpcSignalSema(m_runUpdateSema); } void CControlMgr::WaitForUpdateThread() { PF_AUTO_PUSH_TIMEBAR("CControlMgr::WaitForUpdateThread"); sysIpcWaitSema(m_updateFinishedSema); // Check for pause when the console menu is up (not in multiplayer) // NOTE: The PS4 can tell us our pad is intercepted if the user manually switches off the pad. // We don't want to pause in this situation when exporting a video (url:bugstar:2140784). if( (CPad::IsIntercepted() VIDEO_RECORDING_ENABLED_ONLY(&& !VideoRecording::IsRecording())) || CLiveManager::IsSystemUiShowing() #if !RSG_PC // Do not pause when we are recording a video otherwise we could corrupt it (url:bugstar:2140784). || (GetPlayerPad() && GetPlayerPad()->IsConnected() == false VIDEO_RECORDING_ENABLED_ONLY(&& !VideoRecording::IsRecording())) #endif // !RSG_PC DURANGO_ONLY(|| IsConstrained()) ) { for(s32 i = 0; i < MAX_CONTROL; ++i) { m_controls[i].DisableAllInputs(sm_InputDisableDuration); } if(NetworkInterface::CanPause() #if RSG_PC REPLAY_ONLY(&& !CReplayCoordinator::IsExportingToVideoFile()) #endif ) { if( !fwTimer::IsSystemPaused() ) { #if !__NO_OUTPUT Displayf("CControlMgr::WaitForUpdateThread - about to call fwTimer::StartSystemPause()"); Displayf("CPad::IsIntercepted() = %s", CPad::IsIntercepted()?"true":"false"); Displayf("CLiveManager::IsSystemUiShowing() = %s", CLiveManager::IsSystemUiShowing()?"true":"false"); #if !RSG_PC if (GetPlayerPad()) { Displayf("GetPlayerPad()->IsConnected() = %s", GetPlayerPad()->IsConnected()?"true":"false"); } else { Displayf("GetPlayerPad() returned NULL"); } #endif // !RSG_PC DURANGO_ONLY(Displayf("IsConstrained() = %s", IsConstrained()?"true":"false");) VIDEO_RECORDING_ENABLED_ONLY(Displayf("VideoRecording::IsRecording() = %s", VideoRecording::IsRecording()?"true":"false");) #endif // !__NO_OUTPUT fwTimer::StartSystemPause(); } if(!CPauseMenu::GetPauseRenderPhasesStatus() ) { CPauseMenu::TogglePauseRenderPhases(false, OWNER_CONTROLMGR, __FUNCTION__ ); gSceneRenderingPaused = true; } } } else { if( fwTimer::IsSystemPaused() ) { Displayf("CControlMgr::WaitForUpdateThread - about to call fwTimer::EndSystemPause()"); fwTimer::EndSystemPause(); } if(gSceneRenderingPaused == true && CPauseMenu::GetPauseRenderPhasesStatus() ) { CPauseMenu::TogglePauseRenderPhases(true, OWNER_CONTROLMGR, __FUNCTION__ ); gSceneRenderingPaused = false; } } } // // name: SetProfileChangeFunctions // description: Setup callback functions for profile changes void CControlMgr::SetProfileChangeFunctions(Functor0 init, Functor0 shutdown) { sm_initProfileFn = init; sm_shutdownProfileFn = shutdown; // if the main controller index has already been setup, call the Init function now if(sm_PlayerPadIndex >= 0) { #if __XENON // on PS3, we expect this to occur since the controller index is set to 0 in the Init call. // On 360, this is not expected so log out if we end up here Warningf("CControlMgr :: Controller index setup prior to calling SetProfileChangeFunctions!"); #endif sm_initProfileFn(); } } // // name: CControlMgr::GetNumberOfConnectedPads // description: returns the number of pads that are connected to the console int CControlMgr::GetNumberOfConnectedPads() { int total_number_of_connected_pads = 0; for (int pad_loop = 0; pad_loop < NUM_PADS; pad_loop++) { if (m_pads[pad_loop].IsConnected()) { total_number_of_connected_pads++; } } return total_number_of_connected_pads; } // // name: CControlMgr::SetMainPlayerControl // description: void CControlMgr::SetMainPlayerIndex(s32 index) { bool newIndex = false; // has index changed? if(sm_PlayerPadIndex != index) { // did we have a valid profile previously. If so shutdown previous profile if(sm_PlayerPadIndex != -1) sm_shutdownProfileFn(); newIndex = true; sm_DebugPadIndex = -1; } sm_PlayerPadIndex = index; m_controls[CONTROL_MAIN_PLAYER].SetPad(sm_PlayerPadIndex); Assert(sm_PlayerPadIndex < NUM_PADS); Displayf("Using controller:%d", sm_PlayerPadIndex); // if index change init profile if(newIndex) sm_initProfileFn(); } #if RSG_PC void CControlMgr::UpdateScuiInput() { // SCUI input cannot be updated until the rlPC GamePad Manager is initialized if (g_rlPc.GetGamepadManager() == NULL) return; // Enumerate through our pads for (unsigned i = 0; i < NUM_PADS && i < g_rlPc.GetNumScuiPads(); i++) { rgsc::RgscGamepad* scuiPad = g_rlPc.GetScuiPad(i); if (scuiPad == nullptr) continue; CPad& pad = m_pads[i]; // Update the SCUI pad's connection state with our own scuiPad->SetIsConnected(pad.IsConnected()); // Only update the pad if it is connected if (pad.IsConnected()) { // Pull directly from the underlying pad, since the CPad doesn't update when the SCUI is on screen. scuiPad->SetButtons(pad.m_pPad->GetButtons()); scuiPad->SetAxis(0, (rgsc::u8)pad.m_pPad->GetLeftX()); scuiPad->SetAxis(1, (rgsc::u8)pad.m_pPad->GetLeftY()); scuiPad->SetAxis(2, (rgsc::u8)pad.m_pPad->GetRightX()); scuiPad->SetAxis(3, (rgsc::u8)pad.m_pPad->GetRightY()); scuiPad->SetHasInput(); } } } #endif // RSG_PC // // name: Update // description: Update class void CControlMgr::Update() { sysCriticalSection lock(m_Cs); #if RSG_PC // If the controls are not being updated on the control update thread then we will need to re-capture devices. if(sm_ThreadId == sysIpcCurrentThreadIdInvalid || sm_ThreadId != sysIpcGetCurrentThreadId()) { WIN32PC_ONLY(TELEMETRY_START_ZONE(PZONE_NORMAL, __FILE__,__LINE__,"CControlMgr::Update() - RecaptureLostDevices()")); WIN32PC_ONLY(ioInput::RecaptureLostDevices()); WIN32PC_ONLY(TELEMETRY_END_ZONE(__FILE__,__LINE__)); } if(PARAM_streamingbuild.Get()) { SetMainPlayerIndex(0); } #endif // RSG_PC PF_PUSH_TIMEBAR("INPUT.Update"); WIN32PC_ONLY(TELEMETRY_START_ZONE(PZONE_NORMAL, __FILE__,__LINE__,"CControlMgr::UpdateInputDevices() - ::rage::INPUT.Update")); bool bIgnoreInput = RSG_PC ? (CLiveManager::IsInitialized() && CLiveManager::IsSystemUiShowing()) : false; #if RSG_PC // On PC, we need to update padInput because the SCUI uses it even when on screen. // Otherwise, ignore mouse and keyboard when the SCUI is up. ::rage::INPUT.Update(false, bIgnoreInput, bIgnoreInput, !__WIN32PC); // Hack to get debug cameras working with PS4 pads for working from home. NOTFINAL_ONLY(CControl::EmulateXInputPads()); #else // On consoles, we don't ignore input ::rage::INPUT.Update(); #endif #if RAGE_SEC_ENABLE_YUBIKEY bool listenForYubiKeyInput = UpdateYubikey(); #endif WIN32PC_ONLY(TELEMETRY_END_ZONE(__FILE__,__LINE__)); PF_POP_TIMEBAR(); // TR: TODO: Remove this as it is better to call this from the user/network code. sysUserList::GetInstance().Update(); if(GetMainPlayerIndex() >= 0) { sysUserList::GetInstance().UpdateUserInfo( static_cast(GetMainPlayerIndex()) ); } PF_PUSH_TIMEBAR("Pad.Update"); WIN32PC_ONLY(TELEMETRY_START_ZONE(PZONE_NORMAL, __FILE__,__LINE__,"CControlMgr::UpdateControls() - Update Pads")); if(g_PlayerSwitch.IsActive()) { StopPadsShaking(); } for(s32 i=0; i= 0 NOTFINAL_ONLY(&& !PARAM_disallowdebugpad.Get())) { #if ENABLE_DEBUG_CONTROLS //if we have a player pad but not a debug pad then the next pad that //has input is the debug pad if(sm_DebugPadIndex < 0) { for(s32 i=0; iAreControlsDisabled()) sm_bShakeWhenControllerDisabled = false; PF_PUSH_TIMEBAR("Debug.Update"); // This is able debug input will we are listening for Yubikey input, as that is random keyboard input // it can trigger debug input, that makes it go in to strange states. (this just disabling it at startup, on Yubikey builds) #if RAGE_SEC_ENABLE_YUBIKEY if (!listenForYubiKeyInput) { #endif #if ENABLE_DEBUG_CONTROLS #if !__FINAL XPARAM(nocheats); if (!CScriptDebug::GetDisableDebugCamAndPlayerWarping() && !PARAM_nocheats.Get()) // For build for Image Metrics #endif { if(sm_DebugPadIndex >= 0) { m_debugPad.Update(); } else if(!sm_bDebugPad) { m_debugPad.Clear(); } //Allow toggling of the debug pad on Start+Square/X. CPad* playerPad = GetPlayerPad(); const bool toggleDebug = (m_keyboard.GetKeyJustDown(KEY_D, KEYBOARD_MODE_DEBUG, "Toggle debug pad") || REPLAY_ONLY(m_keyboard.GetKeyJustDown(KEY_D, KEYBOARD_MODE_REPLAY, "Toggle debug pad") ||) (playerPad && (playerPad->GetPressedDebugButtons() & ioPad::RLEFT))); if(toggleDebug) { sm_bDebugPad = !sm_bDebugPad; #if __BANK if(sm_ThreadId != sysIpcCurrentThreadIdInvalid && sysIpcGetCurrentThreadId() == sm_ThreadId && !CSystem::IsThisThreadId(SYS_THREAD_RENDER)) { const char* stringLabel = sm_bDebugPad ? "HELP_DBGPADON" : "HELP_DBGPADOFF"; char *string = TheText.Get(stringLabel); CHelpMessage::SetMessageText(HELP_TEXT_SLOT_STANDARD, string); } #endif // __BANK } if(sm_bDebugPad) { if(sm_PlayerPadIndex >= 0) { m_debugPad.UpdateFromPad(m_pads[sm_PlayerPadIndex]); m_controls[CONTROL_DEBUG].SetPad(sm_PlayerPadIndex); m_controls[CONTROL_DEBUG].Update(); // only update the debug control map bkIo::SetPadIndex(sm_PlayerPadIndex); m_pads[sm_PlayerPadIndex].Clear(); } } else if(toggleDebug && sm_DebugPadIndex >= 0) { m_controls[CONTROL_DEBUG].SetPad(sm_DebugPadIndex); bkIo::SetPadIndex(sm_DebugPadIndex); } } // end of !CTheScripts::GetDisableDebugCamAndPlayerWarping() #if !__FINAL if (CScriptDebug::GetDisableDebugCamAndPlayerWarping()) // For build for Image Metrics { // just to be sure m_debugPad.Clear(); } #endif #endif //ENABLE_DEBUG_CONTROLS #if RAGE_SEC_ENABLE_YUBIKEY } #endif //RAGE_SEC_ENABLE_YUBIKEY PF_POP_TIMEBAR(); #if ENABLE_DEBUG_CONTROLS if(!sm_bDebugPad) #endif { PF_PUSH_TIMEBAR("controls.Update"); WIN32PC_ONLY(TELEMETRY_START_ZONE(PZONE_NORMAL, __FILE__,__LINE__,"CControlMgr::UpdateControls() - controls.Update")); // update all main control maps for each player (note we do not update the debug pad here) for(s32 i=0; i= IDT_AMOUNT) { displayType = 0; } sm_eInputDisplayType = static_cast(displayType); } if(sm_eInputDisplayType != IDT_NONE) { // output colors. const static Color32 enabledColor = Color32(255, 255, 255); const static Color32 disabledColor = Color32(255, 0, 0); // the Y starting position const float ystart = 0.05f; #if DEBUG_DRAW // Bottom bar has two lines of text. const float BottomBarHeight = (3.5f * static_cast(grcDebugDraw::GetScreenSpaceTextHeight())) / static_cast(grcDevice::GetHeight()); // output increments. const float dy = (grcDebugDraw::GetScreenSpaceTextHeight() / static_cast(grcDevice::GetHeight()) * sm_InputDisplayScale); #else const float BottomBarHeight = 0.0f; const float dy = 0.0f; #endif // This number was deduced by zooming in and counting the pixels. const s32 TEXT_WIDTH = 9; // we use 8 as the width. This was deduced from as we cannot get the width so we are assuming the width and hight are the same. const float dx = (TEXT_WIDTH / static_cast(grcDevice::GetWidth())) * sm_InputDislayMaxLength * sm_InputDisplayScale; float ypos = ystart; float xpos = sm_InputDisplayX; // print out the radio/weapon wheels #if __DEV if( const char* pszWeaponWheel = CNewHud::GetWeaponWheelDisabledReason() ) { DebugInputPrint( Vector2(xpos, ypos), disabledColor, pszWeaponWheel, true, sm_InputDisplayScale, sm_InputDisplayScale ); ypos += dy; } if( const char* pszRadioWheel = CNewHud::GetRadioWheelDisabledReason() ) { DebugInputPrint( Vector2(xpos, ypos), disabledColor, pszRadioWheel, true, sm_InputDisplayScale, sm_InputDisplayScale ); ypos += dy; } #endif const CPlayerInfo* pPlayerInfo = GetLocalPlayerInfo(); bool anyControlDisabled = false; // Show information about player control enable state. NOTE: We do not need to check what GetMainPlayerControl() returns as we are going to // check all the checks currently in GetMainPlayerControl(). However doing it this way, if someone edits GetMainPlayerControl() and adds a new // disable condition, we will detect it and inform the reason is unknown. if(&m_controls[CONTROL_EMPTY] == &GetMainPlayerControl()) { bool hasReason = false; anyControlDisabled = true; if(CWarningScreen::IsActive()) { DebugInputPrint( Vector2(xpos, ypos), disabledColor, "Player Control (Warning Screen Visible)", true, sm_InputDisplayScale, sm_InputDisplayScale ); hasReason = true; ypos += dy; } if(CLiveManager::IsSystemUiShowing()) { DebugInputPrint( Vector2(xpos, ypos), disabledColor, "Player Control (System UI Showing)", true, sm_InputDisplayScale, sm_InputDisplayScale ); hasReason = true; ypos += dy; } if(pPlayerInfo == NULL) { DebugInputPrint( Vector2(xpos, ypos), disabledColor, "Player Control (No Local Player Info)", true, sm_InputDisplayScale, sm_InputDisplayScale ); hasReason = true; ypos += dy; } else { if(pPlayerInfo->AreControlsDisabled()) { DebugInputPrint( Vector2(xpos, ypos), disabledColor, "Player Control (Manually Disabled)", true, sm_InputDisplayScale, sm_InputDisplayScale ); hasReason = true; ypos += dy; } if(pPlayerInfo->GetPlayerPed() == NULL) { DebugInputPrint( Vector2(xpos, ypos), disabledColor, "Player Control (No Player Ped)", true, sm_InputDisplayScale, sm_InputDisplayScale ); hasReason = true; ypos += dy; } else if(pPlayerInfo->GetPlayerPed()->GetIsArrested()) { DebugInputPrint( Vector2(xpos, ypos), disabledColor, "Player Control (Player Arrested)", true, sm_InputDisplayScale, sm_InputDisplayScale ); hasReason = true; ypos += dy; } } if(!hasReason) { DebugInputPrint( Vector2(xpos, ypos), disabledColor, "Player Control (Unknown)", true, sm_InputDisplayScale, sm_InputDisplayScale ); ypos += dy; } } // Show information about camera control enable state. NOTE: We do not need to check what GetMainCameraControl() returns as we are going to // check all the checks currently in GetMainCameraControl(). However doing it this way, if someone edits GetMainCameraControl() and adds a new // disable condition, we will detect it and inform the reason is unknown. if(&m_controls[CONTROL_EMPTY] == &GetMainCameraControl()) { bool hasReason = false; anyControlDisabled = true; if(CWarningScreen::IsActive()) { DebugInputPrint( Vector2(xpos, ypos), disabledColor, "Camera Control (Warning Screen Visible)", true, sm_InputDisplayScale, sm_InputDisplayScale ); hasReason = true; ypos += dy; } if(CLiveManager::IsSystemUiShowing()) { DebugInputPrint( Vector2(xpos, ypos), disabledColor, "Camera Control (System UI Showing)", true, sm_InputDisplayScale, sm_InputDisplayScale ); hasReason = true; ypos += dy; } if(pPlayerInfo == NULL) { DebugInputPrint( Vector2(xpos, ypos), disabledColor, "Camera Control (No Local Player Info)", true, sm_InputDisplayScale, sm_InputDisplayScale ); hasReason = true; ypos += dy; } else { if(pPlayerInfo->IsControlsCameraDisabled()) { DebugInputPrint( Vector2(xpos, ypos), disabledColor, "Camera Control (Manually Disabled)", true, sm_InputDisplayScale, sm_InputDisplayScale ); hasReason = true; ypos += dy; } if(NetworkInterface::IsGameInProgress() && pPlayerInfo->IsControlsFrontendDisabled()) { DebugInputPrint( Vector2(xpos, ypos), disabledColor, "Camera Control (Controls Frontend Disabled)", true, sm_InputDisplayScale, sm_InputDisplayScale ); hasReason = true; ypos += dy; } } if(CNewHud::ShouldWheelBlockCamera()) { DebugInputPrint( Vector2(xpos, ypos), disabledColor, "Camera Control (Weapon Wheel)", true, sm_InputDisplayScale, sm_InputDisplayScale ); hasReason = true; ypos += dy; } if(!hasReason) { DebugInputPrint( Vector2(xpos, ypos), disabledColor, "Camera Control (Unknown)", true, sm_InputDisplayScale, sm_InputDisplayScale ); ypos += dy; } } // Show information about frontend control enable state. NOTE: We do not need to check what GetMainFrontendControl() returns as we are going to // check all the checks currently in GetMainFrontendControl(). However doing it this way, if someone edits GetMainFrontendControl() and adds a new // disable condition, we will detect it and inform the reason is unknown. if(&m_controls[CONTROL_EMPTY] == &GetMainFrontendControl()) { bool hasReason = false; anyControlDisabled = true; if(!CLiveManager::IsInitialized()) { DebugInputPrint( Vector2(xpos, ypos), disabledColor, "Frontend Control (Live Manager Not Init)", true, sm_InputDisplayScale, sm_InputDisplayScale ); hasReason = true; ypos += dy; } if(CLiveManager::IsSystemUiShowing()) { DebugInputPrint( Vector2(xpos, ypos), disabledColor, "Frontend Control (System UI Showing)", true, sm_InputDisplayScale, sm_InputDisplayScale ); hasReason = true; ypos += dy; } if(pPlayerInfo != NULL && !CSavegameAutoload::IsPerformingAutoLoadAtStartOfGame()) { DebugInputPrint( Vector2(xpos, ypos), disabledColor, "Frontend Control (No Local Player Info)", true, sm_InputDisplayScale, sm_InputDisplayScale ); hasReason = true; ypos += dy; } if(!hasReason) { DebugInputPrint( Vector2(xpos, ypos), disabledColor, "Camera Control (Unknown)", true, sm_InputDisplayScale, sm_InputDisplayScale ); ypos += dy; } } // Leave a gap between disabled controllers and inputs. if(anyControlDisabled) { ypos += (2 * dy); } const char* pszType = NULL; switch(sm_eInputDisplayType) { case IDT_ALL: pszType = "All Inputs"; break; case IDT_NONZERO: pszType = "Non-Zero Inputs"; break; case IDT_DISABLED_ONLY: pszType = "Disabled Inputs Only"; break; default: controlAssertf(false, "Unknown input display type!"); break; } if( pszType ) { DebugInputPrint( Vector2(xpos, ypos), Color32(0, 255, 0), pszType, true, sm_InputDisplayScale, sm_InputDisplayScale ); ypos += dy; } char buffer[100]; ioValue::ReadOptions options = ioValue::DEFAULT_OPTIONS; options.SetFlags(ioValue::ReadOptions::F_READ_DISABLED, true); // Show inputs enable state and value. for(s32 i = 0; i < MAX_INPUTS; ++i) { if(ypos > (1.0f - dy - BottomBarHeight)) { ypos = ystart; xpos += dx; } buffer[0] = '\0'; const ioValue& value = m_controls[CONTROL_MAIN_PLAYER].GetValue(i); if(sm_eInputDisplayType == IDT_ALL || (sm_eInputDisplayType == IDT_DISABLED_ONLY && !value.IsEnabled()) || (sm_eInputDisplayType == IDT_NONZERO && value.GetUnboundNorm(options) != 0.0f )) { const char* inputName = m_controls[CONTROL_MAIN_PLAYER].GetInputName(static_cast(i)); if(strncmp(inputName, "INPUT_", 6) == 0) { inputName += 6; } formatf(buffer, 100, "%s (%.2f)", inputName, value.GetUnboundNorm(options) ); DebugInputPrint( Vector2(xpos, ypos), (value.IsEnabled() ? enabledColor : disabledColor), buffer, true, sm_InputDisplayScale, sm_InputDisplayScale ); ypos += dy; } } } else { controlAssertf(sm_eInputDisplayType == IDT_NONE, "Unknown display type for outputting input status!"); } } #endif // __BANK #if __BANK static int gFakeKeypress; void FakeKeypress() { CControlMgr::GetKeyboard().FakeKeyPress(gFakeKeypress); } void CControlMgr::InitWidgets() { bkBank& bank = BANKMGR.CreateBank("Input"); bank.AddSlider("Overlay Disable Duration:", &sm_InputDisableDuration, 0U, 5000U, 1U); #if DEBUG_DRAW bank.PushGroup("On Screen Inputs"); bank.AddCombo("Inputs:", reinterpret_cast(&sm_eInputDisplayType), IDT_AMOUNT, sm_InputDislayTypeNames); bank.AddSlider("Text Scale", &sm_InputDisplayScale, 0.0f, 10.0f, 0.01f); bank.AddSlider("Text X Position", &sm_InputDisplayX, -10.0f, 1.0f, 0.01f); bank.PopGroup(); #endif // DEBUG_DRAW // Load a custom meta data file bank.PushGroup("Custom Meta Input File"); bank.AddText("Custom Meta File Path:",sm_CustomInputMetaFilePath, RAGE_MAX_PATH); bank.AddButton("Load Custom Controls", LoadCustomControls); bank.AddButton("Load Default Controls", ReInitControls); bank.PopGroup(); bank.PushGroup("Pad"); bank.AddToggle("Debug pad", &sm_bDebugPad); bank.AddToggle("Debug Pad Values", &sm_bDebugPadValues); bank.AddSlider("Debug Pad Number", &gPadNumberToDebug, 0, NUM_PADS-1, 1); bank.AddSlider("Defaults Setting Num", &sm_nSaveAsDefaultNum, 0, 9, 1); bank.AddButton("Save Defaults", &SaveAsDefaultsButton); bank.AddButton("Load Defaults", &LoadDefaultsButton); const s32 iMaxHardcodedMappings = 3; const char* mappingNames[iMaxHardcodedMappings] = { "Default", "Test1", "Test2", }; bank.AddCombo("HardcodedMapping", &sm_iHardcodedMappingIndex, iMaxHardcodedMappings, mappingNames, HardcodedMappingCallback); bank.AddToggle("Apply frequency for Motor 0", &sm_bOverrideShakeFreq0); bank.AddSlider("Shake Frequency - Motor 0", &sm_iShakeFrequency0, 0, 256, 1); bank.AddToggle("Apply duration for Motor 0", &sm_bOverrideShakeDur0); bank.AddSlider("Duration", &sm_iShakeDuration0, 0, 255, 1); bank.AddToggle("Apply frequency for Motor 1", &sm_bOverrideShakeFreq1); bank.AddSlider("Shake Frequency - Motor 1", &sm_iShakeFrequency1, 0, 256, 1); bank.AddToggle("Apply duration for Motor 1", &sm_bOverrideShakeDur1); bank.AddSlider("Shake Duration", &sm_iShakeDuration1, 0, 255, 1); bank.AddButton("Shake Pad", DebugShakeUsingOverridesCallback); bank.AddToggle("Apply Delay after shake.", &sm_bOverrideShakeDelay); bank.AddSlider("Shake delay after this one", &sm_iShakeDelayAfterThisOne, 0, 2555555, 1); bank.AddToggle("Use widget multiplier", &sm_bUseThisMultiplier); bank.AddSlider("Multiplier", &sm_fMultiplier, 0.0f, 10000.0f, 1.0f); bank.PushGroup("Debug Shake Intensity"); bank.AddSlider("Intensity",&sm_fDebugShakeIntensity,0.0f,1.0f,0.01f); bank.AddSlider("Duration",&sm_iDebugShakeDuration,0,5000,1); bank.AddButton("Do shake",DebugShakeCallback); bank.AddSlider("Motor to shake", &sm_iDebugMotorToShake, 0, ioPad::MAX_ACTUATORS -1, 1); bank.AddToggle("Shake Motor", &sm_bDebugShakeMotor); bank.PopGroup(); bank.PopGroup(); bank.PushGroup("Keyboard"); bank.AddToggle("Show Debug Keys", &gbShowDebugKeys); bank.AddToggle("Show Marketing Debug Keys", &gbShowLevelDebugKeys); bank.AddSlider("Key value", &gFakeKeypress, 0, 255, 1); bank.AddButton("Fake keypress", datCallback(FakeKeypress)); static const char* virtualKeyboardModes [ioVirtualKeyboard::kTextType_COUNT] = { "Default", "Email", "Password", "Numeric", "Alphabetic", "Gamertag" }; bank.AddCombo("Virtual Keyboard Mode", &sm_KeyboardMode, ioVirtualKeyboard::kTextType_COUNT, virtualKeyboardModes); bank.AddSlider("Virtual Keyboard Text Limit", &sm_VirtualKeyboardTextLimit, 0, ioVirtualKeyboard::MAX_STRING_LENGTH_FOR_VIRTUAL_KEYBOARD, 1); bank.AddText("Virtual Keyboard Text", sm_DefaultVirtualKeyboardText, MAX_KEYBOARD_TEXT); bank.AddButton("Show Virtual Keyboard", DebugShowVirtualKeyboard); bank.AddButton("Hide Virtual Keyboard", CancelVirtualKeyboard); bank.PopGroup(); // now show virtual inputs. bank.PushGroup("Channel/Virtual Inputs"); m_controls[CONTROL_MAIN_PLAYER].InitWidgets(bank, "Main Player"); m_controls[CONTROL_EMPTY].InitWidgets(bank, "Empty"); #if ENABLE_DEBUG_CONTROLS m_controls[CONTROL_DEBUG].InitWidgets(bank, "Debug"); #endif // ENABLE_DEBUG_CONTROLS bank.PopGroup(); // for now we are only adding mapping widgets to pc version. #if __WIN32PC // now add the mappings controls. bank.PushGroup("Input Mapping"); m_controls[CONTROL_MAIN_PLAYER].InitMappingWidgets(bank); m_controls[CONTROL_MAIN_PLAYER].InitJoystickCalibrationWidgets(bank); bank.AddButton("Reload Controls", datCallback(CFA(CControlMgr::ReInitControls))); bank.PopGroup(); bank.AddSlider("Mouse Smoothing Frames", &rage::ioMouse::sm_MouseSmoothingFrames, 1, 4, 1); bank.AddSlider("Mouse Smoothing Threshold", &rage::ioMouse::sm_MouseSmoothingThreshold, -1.0f, 255.0f, 1.0f); bank.AddSlider("Mouse Blend out Threshold", &rage::ioMouse::sm_MouseSmoothingBlendLimit, 0.0f, 255.0f, 1.0f); bank.AddSlider("Mouse Smoothing Weight", &rage::ioMouse::sm_MouseWeightScale, 0.01f, 1.0f, 0.005f); #endif // __WIN32PC BANK_ONLY(ioHeadTracking::InitWidgets();) #if __PPU CPad* pad = GetPlayerPad(); if(pad) { CPadGesture* gesture = pad->GetPadGesture(); if(gesture) { gesture->InitWidgets(bank); } } #endif // __PPU #if RSG_ORBIS bank.PushGroup("Orbis Touch Pad"); CTouchPadGesture::InitWidgets(bank); bank.PopGroup(); #endif // RSG_ORBIS } #endif // __BANK // returns local main player's control CControl& CControlMgr::GetMainPlayerControl(bool bAllowDisabling) { CPlayerInfo* pPlayerInfo = GetLocalPlayerInfo(); // If the controls for this player are disabled we return the 'empty' controls. if (CWarningScreen::IsActive() || CLiveManager::IsSystemUiShowing() || !pPlayerInfo || (bAllowDisabling && pPlayerInfo->AreControlsDisabled()) || !pPlayerInfo->GetPlayerPed() || pPlayerInfo->GetPlayerPed()->GetIsArrested() ) { return (m_controls[CONTROL_EMPTY]); } return (m_controls[CONTROL_MAIN_PLAYER]); } // returns local main player's camera control // it is optional whether the controls can be disabled or not... CControl& CControlMgr::GetMainCameraControl(bool bAllowDisabling) { CPlayerInfo* pPlayerInfo = GetLocalPlayerInfo(); // If the camera controls for this player are disabled we return the 'empty' controls. // NOTE: We effectively disable the camera controls when the frontend menu has disabled controls in a network game, as this prevents the // last valid control input from being locked-in while the menu is rendering and the game remains active in the background (not paused.) if(CWarningScreen::IsActive() || CLiveManager::IsSystemUiShowing() || !pPlayerInfo || ( bAllowDisabling && ( pPlayerInfo->IsControlsCameraDisabled() || CNewHud::ShouldWheelBlockCamera() || ( NetworkInterface::IsGameInProgress() && pPlayerInfo->IsControlsFrontendDisabled() ) ))) { return (m_controls[CONTROL_EMPTY]); } return (m_controls[CONTROL_MAIN_PLAYER]); } // returns local main player's control for use with frontend features that don't care whether game control is disabled CControl& CControlMgr::GetMainFrontendControl(bool bAllowDisabling) { if ((!CLiveManager::IsInitialized() || CLiveManager::IsSystemUiShowing()) || (bAllowDisabling && (!GetLocalPlayerInfo()) && (CGame::IsSessionInitialized()) && (!CSavegameAutoload::IsPerformingAutoLoadAtStartOfGame()) ) ) { return (m_controls[CONTROL_EMPTY]); } return (m_controls[CONTROL_MAIN_PLAYER]); } #if ENABLE_DEBUG_CONTROLS // returns local main player's control CControl& CControlMgr::GetDebugControl() { return (m_controls[CONTROL_DEBUG]); } #endif // // name: StopPadsShaking // description: Stop all the pads shaking void CControlMgr::StopPadsShaking() { for(s32 i=0; iGetPlayerState() == CPlayerInfo::PLAYERSTATE_HASDIED && !bFaderStopsVibration) { for(s32 i=0; iGetPlayerState() != CPlayerInfo::PLAYERSTATE_PLAYING) { return true; } // Camera is in slow motion else if (!sm_bShakeWhenControllerDisabled && pPlayerInfo && pPlayerInfo->AreControlsDisabled() && !pPlayerInfo->IsControlsFrontendDisabled() && !pPlayerInfo->IsControlsScriptDisabled() && !pPlayerInfo->IsControlsScriptAmbientDisabled() && !pPlayerInfo->IsControlsCutscenesDisabled()) { return true; } //else if (!sm_bShakeWhenControllerDisabled && pPlayerInfo && // fwTimer::GetTimeWarp() != 1.0f && // !pPlayerInfo->IsControlsScriptDisabled() && // !pPlayerInfo->IsControlsScriptAmbientDisabled() && // !pPlayerInfo->IsControlsCutscenesDisabled()) //{ // return true; //} // Screen is Fading or faded out if (pViewport && bFaderStopsVibration) { return true; } // System UI active else if(CLiveManager::IsSystemUiShowing() && !NetworkInterface::IsGameInProgress()) { return true; } // Game is Paused else if (fwTimer::IsGamePaused() && !CPauseMenu::IsActive()) { return true; } #if GTA_REPLAY // We are in the replay editor or replay-preview else if ( CReplayCoordinator::IsActive() ) { return true; } #endif return false; } void CControlMgr::StartPlayerPadShakeByIntensity( u32 iDuration, float fIntensity, s32 iDelayAfterThisOne, s32 iController ) { fIntensity = rage::Clamp(fIntensity,0.0f,1.0f); // At low intensity only motor 1 plays (it is weaker) // then motor 0 replaces it // ..finally both come in // Tuned for each platform, due to slight hardware differences #if USE_ACTUATOR_EFFECTS #if RSG_ORBIS static dev_float fMinPadShakeFreq0 = 100.0f; static dev_float fMaxPadShakeFreq0 = 255.0f; static dev_float fMinPadShakeFreq1 = 1.0f; static dev_float fMaxPadShakeFreq1 = 255.0f; static dev_float fMotor1Stop = -1.0f; static dev_float fBothMotorStart = 0.9f; #elif RSG_DURANGO static dev_float fMinPadShakeFreq0 = 150.0f; static dev_float fMaxPadShakeFreq0 = 255.0f; static dev_float fMinPadShakeFreq1 = 50.0f; static dev_float fMaxPadShakeFreq1 = 120.0f; static dev_float fMotor1Stop = 0.1f; static dev_float fBothMotorStart = 0.9f; #else // PC / XINPUT static dev_float fMinPadShakeFreq0 = 75.0f; static dev_float fMaxPadShakeFreq0 = 255.0f; static dev_float fMinPadShakeFreq1 = 50.0f; static dev_float fMaxPadShakeFreq1 = 120.0f; static dev_float fMotor1Stop = 0.1f; static dev_float fBothMotorStart = 0.9f; #endif #else #if __PS3 static dev_float fMinPadShakeFreq0 = 100.0f; static dev_float fMaxPadShakeFreq0 = 255.0f; static dev_float fMinPadShakeFreq1 = 1.0f; static dev_float fMaxPadShakeFreq1 = 255.0f; static dev_float fMotor1Stop = -1.0f; // Don't use motor 1 at low end for ps3 static dev_float fBothMotorStart = 0.9f; #else // XENON static dev_float fMinPadShakeFreq0 = 15.0f; static dev_float fMaxPadShakeFreq0 = 200.0f; static dev_float fMinPadShakeFreq1 = 50.0f; static dev_float fMaxPadShakeFreq1 = 120.0f; static dev_float fMotor1Stop = 0.1f; static dev_float fBothMotorStart = 0.9f; #endif #endif // USE_ACTUATOR_EFFECTS u32 iFreq0, iFreq1; if(fIntensity == 0.0f) { iFreq0 = iFreq1 = 0; } else if (fIntensity < fMotor1Stop) { iFreq1 = (u32)(fMinPadShakeFreq1 + ((fMaxPadShakeFreq1 - fMinPadShakeFreq1)*(fIntensity/fMotor1Stop))); iFreq0 = 0; } else if (fIntensity < fBothMotorStart) { iFreq0 = (u32)(fMinPadShakeFreq0 + ((fMaxPadShakeFreq0 - fMinPadShakeFreq0)*fIntensity)); iFreq1 = 0; } else { iFreq0 = (u32)(fMinPadShakeFreq0 + ((fMaxPadShakeFreq0 - fMinPadShakeFreq0)*fIntensity)); iFreq1 = (u32)fMaxPadShakeFreq1; } StartPlayerPadShake(iDuration,iFreq0,iDuration,iFreq1,iDelayAfterThisOne, iController); } #if __BANK void CControlMgr::DebugShakeCallback() { StartPlayerPadShakeByIntensity(sm_iDebugShakeDuration,sm_fDebugShakeIntensity,0); } void CControlMgr::DebugShakeUsingOverridesCallback() { StartPlayerPadShake(sm_iShakeDuration0 * 10, sm_iShakeFrequency0, sm_iShakeDuration1 * 10, sm_iShakeFrequency1); } void CControlMgr::HardcodedMappingCallback() { m_controls[CONTROL_MAIN_PLAYER].SetMappingsTest(sm_iHardcodedMappingIndex); m_controls[CONTROL_EMPTY].SetMappingsTest(sm_iHardcodedMappingIndex); #if ENABLE_DEBUG_CONTROLS m_controls[CONTROL_DEBUG].SetMappingsTest(sm_iHardcodedMappingIndex); #endif } void CControlMgr::DebugShowVirtualKeyboard() { ioVirtualKeyboard::Params params; char16 defaultText[MAX_KEYBOARD_TEXT]; Utf8ToWide(defaultText, sm_DefaultVirtualKeyboardText, MAX_KEYBOARD_TEXT); params.m_InitialValue = defaultText; params.m_KeyboardType = static_cast(sm_KeyboardMode); params.m_Description = _C16("Rag Debug Test"); params.m_Title = _C16("Debug Title"); params.m_MaxLength = MAX_KEYBOARD_TEXT; params.m_PlayerIndex = 0; params.m_MaxLength = sm_VirtualKeyboardTextLimit; ShowVirtualKeyboard(params); sm_bCheckKeyboard = true; } #endif #if DEBUG_PAD_INPUT bool CControlMgr::IsDebugPadValuesOn() { bool bPlayingCutscene = CutSceneManager::GetInstancePtr() && CutSceneManager::GetInstance() && CutSceneManager::GetInstance()->IsRunning(); return sm_bDebugPadValues && !bPlayingCutscene; } #endif void CControlMgr::ShowVirtualKeyboard(const ioVirtualKeyboard::Params& params) { sm_VirtualKeyboardResult[0] = '\0'; // The title in the text box has been removed for the time being. // The title passed into this function will be passed to the input box as its description. // Future Todo: support max lenth and input type masking Displayf("Showing ioVirtualKeyboard!"); ioVirtualKeyboard::Params params2 = params; params2.m_PlayerIndex = sm_PlayerPadIndex != -1 ? sm_PlayerPadIndex : 0; sm_bVirtualKeyboardIsEnteringAPassword = (params.m_KeyboardType == ioVirtualKeyboard::kTextType_PASSWORD); #if RSG_ORBIS params2.m_UserId = g_rlNp.GetUserServiceId(params2.m_PlayerIndex); #endif // work out language for virtual keyboard if(params2.m_AlphabetType == ioVirtualKeyboard::kAlphabetType_INVALID) { switch(CPauseMenu::GetMenuPreference(PREF_CURRENT_LANGUAGE)) { case LANGUAGE_RUSSIAN: params2.m_AlphabetType = ioVirtualKeyboard::kAlphabetType_CYRILLIC; break; case LANGUAGE_CHINESE_TRADITIONAL: params2.m_AlphabetType = ioVirtualKeyboard::kAlphabetType_CHINESE; break; case LANGUAGE_CHINESE_SIMPLIFIED: params2.m_AlphabetType = ioVirtualKeyboard::kAlphabetType_CHINESE_SIMPLIFIED; break; case LANGUAGE_KOREAN: params2.m_AlphabetType = ioVirtualKeyboard::kAlphabetType_KOREAN; break; /* Need 2mb for the memory container on PS3*/ #if __XENON case LANGUAGE_JAPANESE: params2.m_AlphabetType = ioVirtualKeyboard::kAlphabetType_JAPANESE; break; #endif case LANGUAGE_POLISH : params2.m_AlphabetType = ioVirtualKeyboard::kAlphabetType_POLISH; break; default: params2.m_AlphabetType = ioVirtualKeyboard::kAlphabetType_ENGLISH; break; } } #if RSG_PC #if OVERLAY_KEYBOARD // Try and show the big picture keyboard overlay. This can fail when the user is not in big picture mode. In this case we fallback to the // PC text input box. sm_bUsingOverlayKeyboard = sm_VirtualKeyboard.Show(params2); if(sm_bUsingOverlayKeyboard) { return; } #endif // OVERLAY_KEYBOARD STextInputBox::GetInstance().Open(params2); #else Verifyf(sm_VirtualKeyboard.Show(params2), "Failed to show ioVirtualKeyboard!"); #endif // RSG_PC } void CControlMgr::ReplaceCharacterIfUnsupportedInFont(char16 &input, s32 fontStyle, eFontBitField fontBit, const char* OUTPUT_ONLY(pFontName), int OUTPUT_ONLY(characterIndex)) { if ((sm_fontBitField & fontBit) != 0) { if (!CTextFormat::DoesCharacterExistInFont(fontStyle, input)) { Displayf("***GRAEME*** CControlMgr::ReplaceCharacterIfUnsupportedInFont - character %d (%c) doesn't exist in the %s font so I'll replace it with a space", characterIndex, input, pFontName); input = ' '; } } } // Copied from rage::WideToUtf8 // Replace a few characters that would cause us problems such as ~ and characters that could be used for HTML // Should we avoid any foreign language characters? char *CControlMgr::WideToSafeUtf8(char *out,const char16 *in,int length) { u8 * utf8out = (u8*)out; for (int i=0; in[i] && i' : case '\\' : case '\"' : case 0xa4 : // microsoft MS points symbol input = '-'; break; case 0xA6 : // '¦' // swap this out for '|' which is supported input = '|'; } } // if (!sm_bVirtualKeyboardIsEnteringAPassword) if (input<=0x7F) *utf8out++ = (u8)input; else if (input <= 0x7FF) { *utf8out++ = u8(0xC0 + (input >> 6)); *utf8out++ = u8(0x80 + (input & 0x3f)); } else { *utf8out++ = u8(0xE0 + (input >> 12)); *utf8out++ = u8(0x80 + ((input >> 6)&0x3f)); *utf8out++ = u8(0x80 + (input & 0x3f)); } } *utf8out = 0; return out; } ioVirtualKeyboard::eKBState CControlMgr::UpdateVirtualKeyboard() { #if !RSG_PC || OVERLAY_KEYBOARD #if OVERLAY_KEYBOARD if(sm_bUsingOverlayKeyboard) #endif // OVERLAY_KEYBOARD { if (sm_VirtualKeyboard.IsPending()) { sm_VirtualKeyboard.Update(); if (sm_VirtualKeyboard.Succeeded()) { Displayf("ioVirtualKeyboard succeeded, getting result."); char16* kbdData = sm_VirtualKeyboard.GetResult(); WideToSafeUtf8(sm_VirtualKeyboardResult, kbdData, ioVirtualKeyboard::MAX_STRING_LENGTH_FOR_VIRTUAL_KEYBOARD); Displayf("ioVirtualKeyboard result was, %s.", sm_VirtualKeyboardResult); } if (!sm_VirtualKeyboard.IsPending()) { sm_fontBitField = 0; // Reset this for the next time the onscreen keyboard is displayed } } Assertf( sm_VirtualKeyboard.GetState() != ioVirtualKeyboard::kKBState_INVALID || sm_VirtualKeyboard.GetState() != ioVirtualKeyboard::kKBState_FAILED, "Failed in updating ioVirtualKeyboard, state is %d!", sm_VirtualKeyboard.GetState() ); return sm_VirtualKeyboard.GetState(); } #endif // !RSG_PC || OVERLAY_KEYBOARD #if RSG_PC // PC Input box has a registered update call ioVirtualKeyboard::eKBState eState = STextInputBox::GetInstance().GetState(); if(eState == ioVirtualKeyboard::kKBState_SUCCESS) { CTextInputBox& textBoxInstance = STextInputBox::GetInstance(); char16 const * const c_textResult = textBoxInstance.GetTextChar16(); int const c_wideLength = c_textResult ? (int)wcslen( c_textResult ) : 0; int const c_maxLength = Min( c_wideLength, (int)textBoxInstance.GetMaxLength() ); int convertedCount(0); WideToUtf8( sm_VirtualKeyboardResult, c_textResult, c_maxLength, ioVirtualKeyboard::VIRTUAL_KEYBOARD_RESULT_BUFFER_SIZE, &convertedCount ); Utf8RemoveInvalidSurrogates( sm_VirtualKeyboardResult ); } return eState; #endif // RSG_PC } void CControlMgr::CancelVirtualKeyboard() { #if !RSG_PC sm_VirtualKeyboard.Cancel(); #else #if OVERLAY_KEYBOARD if(sm_bUsingOverlayKeyboard) { sm_VirtualKeyboard.Cancel(); } else #endif // OVERLAY_KEYBOARD { STextInputBox::GetInstance().Close(); } #endif // !RSG_PC } char* CControlMgr::GetVirtualKeyboardResult() { return sm_VirtualKeyboardResult; } ioVirtualKeyboard::eKBState CControlMgr::GetVirtualKeyboardState() { #if RSG_PC #if OVERLAY_KEYBOARD if(sm_bUsingOverlayKeyboard) { return sm_VirtualKeyboard.GetState(); } #endif // OVERLAY_KEYBOARD return STextInputBox::GetInstance().GetState(); #else return sm_VirtualKeyboard.GetState(); #endif } #if RSG_DURANGO void CControlMgr::HandleConstrainedChanged( sysServiceEvent* evt ) { if(evt != NULL) { if(evt->GetType() == sysServiceEvent::CONSTRAINED) { sysCriticalSection lock(sm_ConstrainedCs); sm_IsGameConstrained = true; } else if(evt->GetType() == sysServiceEvent::UNCONSTRAINED) { sysCriticalSection lock(sm_ConstrainedCs); sm_IsGameConstrained = false; } } } #endif // RSG_DURANGO bool CControlMgr::IsShowingKeyboardTextbox() { #if RSG_PC return STextInputBox::GetInstance().IsActive(); #else return false; #endif // RSG_PC } #if RAGE_SEC_ENABLE_YUBIKEY bool CControlMgr::UpdateYubikey() { // Wait for the legal screen to finish displaying. if (!CLoadingScreens::IsDisplayingLegalScreen()) { return CYubiKeyManager::GetInstance()->Update(); } else { return true; } } #endif //RAGE_SEC_ENABLE_YUBIKEY bool CControlMgr::CheckForControllerDisconnect() { #if !RSG_PC // Pause on controller disconnect unless we are recording a video otherwise we could corrupt it (url:bugstar:2140784). if (CControlMgr::GetPlayerPad() && !CControlMgr::GetPlayerPad()->IsConnected(PPU_ONLY(true)) VIDEO_RECORDING_ENABLED_ONLY(&& !VideoRecording::IsRecording())) { #if RSG_PS3 ++iNumFramesControllerDisconnected; iNumFramesControllerDisconnected = rage::Min(iNumFramesControllerDisconnected, CONTROLLER_RECONNECT_WARNING_THRESHOLD); if (iNumFramesControllerDisconnected >= CONTROLLER_RECONNECT_WARNING_THRESHOLD) #endif //RSG_PS3 { #if RSG_ORBIS if (CControlMgr::GetPlayerPad()->HasBeenConnected()) { CWarningScreen::SetControllerReconnectMessageWithHeader(WARNING_MESSAGE_STANDARD, "SG_HDNG", "PAD_REASSIGNED", FE_WARNING_NONE); } else #elif RSG_DURANGO // NOTE: We do not show this message when we are constrained. This is to stop the controller disconnected message showing when instant on // is enabled. This for url:bugstar:2024506 but instead of clearing the message, we just stop the problematic message. if(g_SysService.IsConstrained() == false) #endif // RSG_ORBIS { CWarningScreen::SetControllerReconnectMessageWithHeader(WARNING_MESSAGE_STANDARD, "SG_HDNG", "NO_PAD", FE_WARNING_NONE); } sm_bNoPadMessageActive = true; } } else { #if RSG_PS3 iNumFramesControllerDisconnected = 0; #endif //RSG_PS3 sm_bNoPadMessageActive = false; } #endif // !RSG_PC return sm_bNoPadMessageActive; }