// // name: ControlMgr.h // description: Class encapsulating all game controllers (pads, mouse, keyboard) etc #ifndef CONTROL_MGR_H_ #define CONTROL_MGR_H_ // Rage headers #include "atl\functor.h" #include "grcore\debugdraw.h" #include "input\input.h" #include "input\mapper.h" #include "input\keys.h" #include "input/virtualkeyboard.h" #include "system/ipc.h" #include "system/criticalsection.h" // Game headers #include "frontend/PauseMenu.h" #include "system/control.h" #include "system/pad.h" #include "system/keyboard.h" #include "system/service.h" #define STANDARD_USER_DEFINED_CONTROLS_FILENAME "common:/data/controls/controls" #define STANDARD_DEFAULT_CONTROLS_FILENAME "common:/data/controls/default" // gets appended with the config number #define ENABLE_DEBUG_CONTROLS (!__FINAL) // using this we can change the defaults file used. Over 2 means custom enum { CONTROLS_CONFIG_DEFAULT = 0, CONTROLS_CONFIG_360_STD, CONTROLS_CONFIG_360_NEW, CONTROLS_CONFIG_MAX }; #define NUM_PADS (ioPad::MAX_PADS) enum { // NOTE: - If you add other player controls then add it to the bottom of CControlMgr::InitWidgets(). CONTROL_MAIN_PLAYER = 0, CONTROL_EMPTY, // All controls in here are clear. This is used when the controls of this player are disabled. #if ENABLE_DEBUG_CONTROLS CONTROL_DEBUG, // additional control layout for debug pad on PC #endif MAX_CONTROL }; #define OVERLAY_KEYBOARD (RSG_PC && __STEAM_BUILD) #if OVERLAY_KEYBOARD #define OVERLAY_KEYBOARD_ONLY(...) __VA_ARGS__ #else #define OVERLAY_KEYBOARD_ONLY(...) #endif // OVERLAY_KEYBOARD #define NUM_PLAYERS (1) // note this does NOT include the debug map enum eCONTROL_METHOD { CONTROL_METHOD_JOYPAD = 0, CONTROL_METHOD_MOUSE_KEYS, MAX_CONTROL_METHODS }; class CControlMgr { friend class CControl; public: static void Init(unsigned initMode); static void ReInitControls(); static void SetDefaultLookInversions(); static void Shutdown(unsigned shutdownMode); static void Update(); static void StartUpdateThread(); static void WaitForUpdateThread(); #if RSG_PC static void UpdateScuiInput(); #endif #if __BANK static void InitWidgets(); static void LoadCustomControls(); #endif static CPad& GetDebugPad() {return m_debugPad;} static CPad* GetPlayerPad() { if(sm_PlayerPadIndex != -1) return &m_pads[sm_PlayerPadIndex]; return NULL; } static int GetNumberOfConnectedPads(); static CKeyboard& GetKeyboard() {return m_keyboard;} static bool IsDisabledControl(CControl *pC) {return ((&m_controls[CONTROL_EMPTY]) == pC);} static void SetMainPlayerIndex(s32 index); static s32 GetMainPlayerIndex() {return sm_PlayerPadIndex;} static CControl& GetMainPlayerControl(bool bAllowDisabling = true);// returns local main player's control static CControl& GetMainFrontendControl(bool bAllowDisabling = true);// returns local main player's control for frontend (dont mind whether the controls are disabled) static CControl& GetMainCameraControl(bool bAllowDisabling = true); // Camera controls need to be independently disabled on some rare occasions i'm afraid - DW static CControl& GetEmptyControl() { return m_controls[CONTROL_EMPTY]; }// returns empty controls // NOTE: This should only be using for retrieving control mappings when player control might be disabled. static CControl& GetPlayerMappingControl() { return m_controls[CONTROL_MAIN_PLAYER]; } #if ENABLE_DEBUG_CONTROLS static CControl& GetDebugControl(); // returns the debug control #endif #if HAS_TRIGGER_RUMBLE static void StartPlayerPadTriggerShake(u32 durationLeft, s32 freqLeft, u32 durationRight, s32 freqRight, s32 controller = CONTROL_MAIN_PLAYER ); #endif // #if HAS_TRIGGER_RUMBLE static void StartPlayerPadShake(u32 Duration0, s32 MotorFrequency0 = 0, u32 Duration1 = 0, s32 MotorFrequency1 = 0, s32 DelayAfterThisOne = 0, s32 controller = CONTROL_MAIN_PLAYER); static void StartPlayerPadShake_Distance(u32 Duration, s32 Frequency, float X, float Y, float Z, s32 controller = CONTROL_MAIN_PLAYER); static void StartPlayerPadShaking(s32 MotorFrequency0 = 0, s32 MotorFrequency1 = 0, s32 controller = CONTROL_MAIN_PLAYER); // This will scale up rumble depending on the platform to fit an intensity value between 0 and 1 static void StartPlayerPadShakeByIntensity(u32 iDuration, float fIntensity, s32 iDelayAfterThisOne = 0, s32 iController = CONTROL_MAIN_PLAYER); static void StopPlayerPadShaking(bool bForce = false, s32 iController = CONTROL_MAIN_PLAYER); static bool IsPlayerPadShaking(s32 controller = CONTROL_MAIN_PLAYER); static void StopPadsShaking(); static void AllowPadsShaking(); static bool ShouldStopPadsShaking(); static void SetShakeWhenControllerDisabled(bool bShake = true); #if __WIN32PC static void ApplyDirectionalForce(float X, float Y, s32 timeMS, s32 controller = CONTROL_MAIN_PLAYER); #endif // __WIN32PC #if DEBUG_PAD_INPUT static void SetDebugPadOn(bool bOn) {sm_bDebugPad = bOn;} static bool IsDebugPadOn() {return sm_bDebugPad;} static bool IsDebugPadValuesOn(); #endif static void SetProfileChangeFunctions(Functor0 init, Functor0 shutdown); static ioVirtualKeyboard::eKBState GetVirtualKeyboardState(); static char* GetVirtualKeyboardResult(); static void ShowVirtualKeyboard(const ioVirtualKeyboard::Params& params); static ioVirtualKeyboard::eKBState UpdateVirtualKeyboard(); static void CancelVirtualKeyboard(); #if !__FINAL static void SetAllowTildeCharacterFromVirtualKeyboard(bool bAllowTilde) { sm_bAllowTildeCharacterFromVirtualKeyboard = bAllowTilde; } #endif // !__FINAL static void SetFontBitFieldForDisplayingOnscreenKeyboardResult(s32 fontBitField) { sm_fontBitField = fontBitField; } #if RSG_DURANGO static bool IsConstrained() { sysCriticalSection lock(sm_ConstrainedCs); return sm_IsGameConstrained; } #endif // RSG_DURANGO #if RAGE_SEC_ENABLE_YUBIKEY static bool UpdateYubikey(); #endif // RAGE_SEC_ENABLE_YUBIKEY static bool IsShowingControllerDisconnectMessage() { return sm_bNoPadMessageActive; } static bool CheckForControllerDisconnect(); static bool IsShowingKeyboardTextbox(); private: enum eFontBitField { // FONT_BIT_STANDARD = 1, // The game already replaces characters in the onscreen keyboard result that aren't supported in the Standard font FONT_BIT_CURSIVE = 2, FONT_BIT_ROCKSTAR_TAG = 4, FONT_BIT_LEADERBOARD = 8, FONT_BIT_CONDENSED = 16, FONT_BIT_FIXED_WIDTH_NUMBERS = 32, FONT_BIT_CONDENSED_NOT_GAMERNAME = 64, FONT_BIT_PRICEDOWN = 128 // gta font }; static CPad& GetPad(s32 num) {return m_pads[num];} static CControl& GetControl(s32 num) {return m_controls[num];} static void SaveAsDefaultsButton(); static void LoadDefaultsButton(); static void ReplaceCharacterIfUnsupportedInFont(char16 &input, s32 fontStyle, eFontBitField fontBit, const char* pFontName, int characterIndex); static char *WideToSafeUtf8(char *out,const char16 *in,int length); static void UpdateThread(void* param); #if RSG_DURANGO static void HandleConstrainedChanged(sysServiceEvent* evt); #endif // RSG_DURANGO static sysIpcThreadId m_updateThreadId; static sysIpcCurrentThreadId sm_ThreadId; static sysIpcSema m_runUpdateSema; static sysIpcSema m_updateFinishedSema; static sysCriticalSectionToken m_Cs; static CControl m_controls[MAX_CONTROL]; static CPad m_pads[NUM_PADS]; static CPad m_debugPad; static CKeyboard m_keyboard; // static eCONTROL_METHOD m_ControlMethod; static bool sm_bDebugPad; static s32 sm_PlayerPadIndex; static s32 sm_DebugPadIndex; static bool sm_bNoPadMessageActive; static volatile bool sm_bShutdownThreads; #if DEBUG_PAD_INPUT static bool sm_bDebugPadValues; #endif // The duration to disable inputs for when the input is intercepted (xbox/ps3 overlay is active). static bank_u32 sm_InputDisableDuration; static Functor0 sm_initProfileFn; static Functor0 sm_shutdownProfileFn; static bool sm_bShakeWhenControllerDisabled; // Used to override controller disabled check static ioVirtualKeyboard sm_VirtualKeyboard; static char sm_VirtualKeyboardResult[ioVirtualKeyboard::VIRTUAL_KEYBOARD_RESULT_BUFFER_SIZE]; static bool sm_bVirtualKeyboardIsEnteringAPassword; #if OVERLAY_KEYBOARD // We are using the overlay keyboard (e.g. steam big picture keyboard). static bool sm_bUsingOverlayKeyboard; #endif // USING_OVERLAY_KEYBOARD #if RSG_DURANGO static sysCriticalSectionToken sm_ConstrainedCs; static ServiceDelegate sm_Delegate; static bool sm_IsGameConstrained; #endif // RSG_DURANGO #if __BANK // Used to test the on screen keyboard. const static u32 MAX_KEYBOARD_TEXT = 25; static bool sm_bCheckKeyboard; static char sm_DefaultVirtualKeyboardText[MAX_KEYBOARD_TEXT]; static int sm_VirtualKeyboardTextLimit; static s32 sm_KeyboardMode; static void DebugShowVirtualKeyboard(); #endif // __BANK #if __BANK public: static void PrintDisabledInputsToGameplayLog(); static bool IsPrintingToGameplayLog() { return sm_bPrintToGameplayLog; } private: static void DisplayDisabledInputs(); // Updated sm_InputDislayTypeNames if a new mode is added. enum InputDisplayType { IDT_NONE, IDT_ALL, IDT_NONZERO, IDT_DISABLED_ONLY, // The number of display options. IDT_AMOUNT }; // This is a text description of the enum above for rag. This must be updated if a new mode is added. static const char* sm_InputDislayTypeNames[IDT_AMOUNT]; static InputDisplayType sm_eInputDisplayType; static float sm_InputDisplayScale; static float sm_InputDisplayX; static s32 sm_InputDislayMaxLength; static bool sm_bPrintToGameplayLog; #endif // __BANK #if !__FINAL static bool sm_bAllowTildeCharacterFromVirtualKeyboard; #endif // !__FINAL static s32 sm_fontBitField; // Taken from eFontBitField public: #if __BANK static s32 sm_nSaveAsDefaultNum; static u32 sm_iShakeDuration0; static u32 sm_iShakeDuration1; static u32 sm_iShakeFrequency0; static u32 sm_iShakeFrequency1; static s32 sm_iShakeDelayAfterThisOne; static float sm_fMultiplier; static bool sm_bOverrideShakeFreq0; static bool sm_bOverrideShakeFreq1; static bool sm_bOverrideShakeDur0; static bool sm_bOverrideShakeDur1; static bool sm_bOverrideShakeDelay; static bool sm_bUseThisMultiplier; static float sm_fDebugShakeIntensity; static u32 sm_iDebugShakeDuration; static u32 sm_iDebugMotorToShake; static bool sm_bDebugShakeMotor; static void DebugShakeCallback(); static void DebugShakeUsingOverridesCallback(); // Path to custom .meta input settings file static char sm_CustomInputMetaFilePath[RAGE_MAX_PATH]; // Used to switch between different hard coded controller mappings static s32 sm_iHardcodedMappingIndex; static void HardcodedMappingCallback(); #endif }; #if HAS_TRIGGER_RUMBLE inline void CControlMgr::StartPlayerPadTriggerShake(u32 durationLeft, s32 freqLeft, u32 durationRight, s32 freqRight, s32 controller ) { m_controls[controller].StartTriggerShake( durationLeft, freqLeft, durationRight, freqRight ); } #endif // HAS_TRIGGER_RUMBLE inline void CControlMgr::StartPlayerPadShake(u32 Duration0, s32 MotorFrequency0, u32 Duration1, s32 MotorFrequency1, s32 DelayAfterThisOne, s32 controller) { #if __BANK if (sm_bOverrideShakeFreq0) MotorFrequency0 = sm_iShakeFrequency0; if (sm_bOverrideShakeFreq1) MotorFrequency1 = sm_iShakeFrequency1; if (sm_bOverrideShakeDur0) Duration0 = sm_iShakeDuration0; if (sm_bOverrideShakeDur1) Duration1 = sm_iShakeDuration1; if (sm_bOverrideShakeDelay) DelayAfterThisOne = sm_iShakeDelayAfterThisOne; #endif // __BANK m_controls[controller].StartPlayerPadShake(Duration0, MotorFrequency0, Duration1, MotorFrequency1, DelayAfterThisOne); } inline void CControlMgr::StartPlayerPadShake_Distance(u32 Duration, s32 Frequency, float X, float Y, float Z, s32 controller) { #if __BANK if (sm_bOverrideShakeFreq0) Frequency = sm_iShakeFrequency0; if (sm_bOverrideShakeFreq1) Frequency = sm_iShakeFrequency1; if (sm_bOverrideShakeDur0) Duration = sm_iShakeDuration0; if (sm_bOverrideShakeDur1) Duration = sm_iShakeDuration1; #endif // __BANK m_controls[controller].StartPlayerPadShake_Distance(Duration, Frequency, X, Y, Z); } inline void CControlMgr::StartPlayerPadShaking(s32 MotorFrequency0, s32 MotorFrequency1, s32 controller) { #if __BANK if (sm_bOverrideShakeFreq0) MotorFrequency0 = sm_iShakeFrequency0; if (sm_bOverrideShakeFreq1) MotorFrequency1 = sm_iShakeFrequency1; #endif // __BANK m_controls[controller].StartShaking(MotorFrequency0, MotorFrequency1); } inline void CControlMgr::StopPlayerPadShaking(bool bForce, s32 iController) { m_controls[iController].StopPlayerPadShaking(bForce); } inline bool CControlMgr::IsPlayerPadShaking(s32 controller) { return m_controls[controller].IsPlayerPadShaking(); } inline void CControlMgr::SetShakeWhenControllerDisabled(bool bShake) { sm_bShakeWhenControllerDisabled = bShake; } #if __WIN32PC inline void CControlMgr::ApplyDirectionalForce(float X, float Y, s32 timeMS, s32 controller) { m_controls[controller].ApplyDirectionalForce(X, Y, timeMS); } #endif // __WIN32PC #endif