// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Math/MathFwd.h" #include "CoreTypes.h" #include "Containers/Map.h" #include "Misc/CoreMiscDefines.h" // For FInputDeviceId/FPlatformUserId #include "GenericPlatform/GenericApplicationMessageHandler.h" #if GAME_INPUT_SUPPORT THIRD_PARTY_INCLUDES_START #include "Microsoft/AllowMicrosoftPlatformTypes.h" #include #include "Microsoft/HideMicrosoftPlatformTypes.h" THIRD_PARTY_INCLUDES_END #include "Microsoft/COMPointer.h" #endif // GAME_INPUT_SUPPORT #if GAME_INPUT_SUPPORT /** * A Game Input Device Processor is used to determine the state of a device * each frame and send messages to the given Message Handler when we want to. * Each processor represents a single "kind" of Game Input device. * * They are created by the owning GameInput Device Container (GameInputDeviceContainer.h) * and polled each frame when possible. Each Human Interface Device can be made up of multiple * Game Input Kinds, so a single device container may have multiple different processors associated with it. */ class IGameInputDeviceProcessor { public: GAMEINPUTBASE_API IGameInputDeviceProcessor(); virtual ~IGameInputDeviceProcessor() = default; struct FGameInputEventParams { /** The current reading for this input event */ IGameInputReading* Reading = nullptr; /** The previous reading from the last frame. */ IGameInputReading* PreviousReading = nullptr; /** Device associated with this reading */ IGameInputDevice* Device = nullptr; /** The message handler to use to send any input events */ TSharedPtr MessageHandler; /** The platform user that is associated with the input device that triggered this event */ FPlatformUserId PlatformUserId = PLATFORMUSERID_NONE; /** The Input device ID of the device that triggered this event. */ FInputDeviceId InputDeviceId = INPUTDEVICEID_NONE; /** Gets the Input Device Info for this GameInput Device if it is valid. Can be null. */ GAMEINPUTBASE_API const GameInputDeviceInfo* GetDeviceInfo() const; }; /** * Process any input events from the given reading and send events to the message handler * using the given Platform User and Input Device Id's. * * This can be called multiple times per frame, like if we are running at a lower FPS. * This is because GameInput may accumulate multiple readings to use by the time the game thread gets here. * * If you have inputs that you only want to process once per frame, then use PostProcessInput instead. * * @return True if any input events have been processed, false if nothing happened */ virtual bool ProcessInput(const FGameInputEventParams& Params) = 0; /** * Called after all current Game Input readings have been processed * this frame and there are no more readings for a device (GetNextReading returns GAMEINPUT_E_READING_NOT_FOUND). * This is called regardless of if an accompanying ProcessInput call has happened. * * This function is useful for dealing with multiple readings in a single frame for analog devices. * It is recommended that processors override this function and only process analog input events here at * the end of the frame, once, instead of multiple times in ProcessInput. That is because at lower frame rates * or hitches you will end up with more then one GameInput reading in the stack that we need to process. If you were to * process all of those analog inputs in a single frame, the values would accumulate to above or below +-1.0, * which could have unexpected behavior in Slate. * * @param Params Game Input event params sent from the owning FGameInputDeviceContainer. * Params.Reading should always be null here, as there is no current reading. * Params.PreviousReading will contain the most recent Game Input reading from the input stack. * * @return True if any input events have been processed, false if nothing happened */ GAMEINPUTBASE_API virtual bool PostProcessInput(const FGameInputEventParams& Params); /** * Clear any input state related to this processor, typically by sending events to the * message handler that each FKey related to this processor now has a value of 0. */ virtual void ClearState(const FGameInputEventParams& Params) = 0; /* * Returns the kind of reading that this processor supports. If the current GameInput reading * is not included in this bitmask, then this processor will not not have it's "ProcessInput" * function called. * * @see FGameInputDeviceContainer::ProcessInput */ virtual GameInputKind GetSupportedReadingKind() const = 0; protected: /** * Returns the string hardware device identifier for the given input event params that can be used * to create an FInputDeviceScope */ GAMEINPUTBASE_API const FString& GetHardwareDeviceIdentifierName(const IGameInputDeviceProcessor::FGameInputEventParams& Params) const; /** A general use function to call the message handler and tell it about a controller analog key being used */ GAMEINPUTBASE_API void OnControllerAnalog(const FGameInputEventParams& Params, const FName& GamePadKey, float NewAxisValueNormalized, float OldAxisValueNormalized, float DeadZone, const bool bSetDeviceScope = true); /** * Helper function for processing the button states of Game input. * * @param CurrentButtonHeldMask The current state of the button mask to evaluate * @param PreviousButtonMask The previous state of the button mask. This value will be modified to store the current button mask after evaluation * @param RepeatTime A map of the button index to a time that it was last active, used to determine if the key meets the qualifications for a repeat event * @param UnrealButtonNameMap A map of Unreal Engine Gamepad key names to their uint32 mapping from Game Input. * @param SupportedButtonCount The maximum number of buttons we can process */ GAMEINPUTBASE_API void EvaluateButtonStates( const FGameInputEventParams& Params, const uint32 CurrentButtonHeldMask, uint32& PreviousButtonMask, double* RepeatTime, const TMap& UnrealButtonNameMap, const uint32 SupportedButtonCount); /** * Helper function to processing the state of a Switch position (aka a DPad, like left/right/up/down) * * @param Params The GameInput event params * @param CurrentPosition The current position of the switch * @param PreviousPosition The previous position of the switch. This will be set to the value of the current switch at the end of this function * @param RepeatTimes Array of doubles that represent the time at which a switch was last pressed that we can use to check for when to send repeat events */ GAMEINPUTBASE_API void EvaluateSwitchState( const FGameInputEventParams& Params, GameInputSwitchPosition CurrentPosition, GameInputSwitchPosition& PreviousPosition, TArray& RepeatTimes); /** Delay before sending a repeat message after a button was first pressed */ float InitialButtonRepeatDelay; /** Delay before sending a repeat message after a button has been pressed for a while */ float ButtonRepeatDelay; }; /** * Processor for Gamepad Inputs */ class FGameInputGamepadDeviceProcessor : public IGameInputDeviceProcessor { protected: GAMEINPUTBASE_API virtual bool ProcessInput(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual bool PostProcessInput(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual void ClearState(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual GameInputKind GetSupportedReadingKind() const override; GAMEINPUTBASE_API bool ProcessGamepadAnalogState(const FGameInputEventParams& Params, GameInputGamepadState& GamepadState); GAMEINPUTBASE_API virtual bool ProcessGamepadButtonState(const FGameInputEventParams& Params, GameInputGamepadState& GamepadState); GameInputGamepadState PreviousState = {}; uint32 LastButtonHeldMask = 0; uint32 LastAnalogButtonHeldMask = 0; enum { MaxSupportedButtons = 32 }; double RepeatTime[MaxSupportedButtons]; /** * Keeps track of how many times this gamepad has been processed this frame. * Every successful processing of button input in ProcessInput will increment this value. * This will get reset at the end of the input frame in PostProcessInput. */ int32 NumReadingsProcessedThisFrame = 0; }; struct FGameInputDeviceConfiguration; /** * Processor for Controller Input Types * * Controllers are very similar to Gamepad types, and often can just be * a third party controller type that is not natively supported by the "GamepadInputKind". * This can include things like instruments, other platform controller types, or other * third party input devices. */ class FGameInputControllerDeviceProcessor : public IGameInputDeviceProcessor { public: GAMEINPUTBASE_API FGameInputControllerDeviceProcessor(); protected: GAMEINPUTBASE_API virtual bool ProcessInput(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual bool PostProcessInput(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual void ClearState(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual GameInputKind GetSupportedReadingKind() const override; /** Processes Controller buttons (often face buttons, like ABXY or Circle/Triangle/Square/X) */ GAMEINPUTBASE_API bool ProcessControllerButtonState(const FGameInputEventParams& Params, const FGameInputDeviceConfiguration* Config, IGameInputReading* InputReading); /** * Process any axis readings on this controller. * This will only process axises that are configured in the given Config. */ GAMEINPUTBASE_API bool ProcessControllerAxisState(const FGameInputEventParams& Params, const FGameInputDeviceConfiguration* Config, IGameInputReading* InputReading); /** * Process the controller switch (also known as the DPad) state */ GAMEINPUTBASE_API bool ProcessControllerSwitchState(const FGameInputEventParams& Params, const FGameInputDeviceConfiguration* Config, IGameInputReading* InputReading); /** A map of GameInputLabel types to Unreal Engine FKey names. */ static GAMEINPUTBASE_API const TMap& GetGameInputButtonLabelToUnrealName(); /** Bitmask of any held buttons from the last frame */ uint32 LastButtonHeldMask = 0; /** Max number of buttons this processor supports */ enum { MaxSupportedButtons = 32 }; /** Timings of when a key press should be considered a "repeat" key */ double RepeatTime[MaxSupportedButtons]; /** Repeat times for when switches are pressed (aka the DPad) */ TArray SwitchRepeatTimes; /** Previous frame's axis values */ TArray PreviousControllerAxisValues; /** Array of switch positions */ TArray PreviousSwitchPositions; /** * Keeps track of how many times this gamepad has been processed this frame. * Every successful processing of button input in ProcessInput will increment this value. * This will get reset at the end of the input frame in PostProcessInput. */ int32 NumReadingsProcessedThisFrame = 0; }; /** * Processor for Keyboard inputs */ class FGameInputKeyboardDeviceProcessor : public IGameInputDeviceProcessor { protected: GAMEINPUTBASE_API virtual bool ProcessInput(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual void ClearState(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual GameInputKind GetSupportedReadingKind() const override; GAMEINPUTBASE_API void UpdateUnifiedKeyboardState(const FGameInputEventParams& Params, TSet& CurrentPressedKeys); inline bool IsKeyPressed(uint8 VirtualKeyCode) const { return LastPressedKeys.Contains(VirtualKeyCode); } GAMEINPUTBASE_API virtual void SetSimulatedCapsLock(bool bVal); TSet LastPressedKeys; TMap KeyRepeatTime; // cannot read actual caps lock state, so necessary to simulate it as a bool toggle bool bSimulatedCapsLock; }; /** * Processor for mouse inputs */ class FGameInputMouseDeviceProcessor : public IGameInputDeviceProcessor { public: GAMEINPUTBASE_API explicit FGameInputMouseDeviceProcessor(const TSharedPtr& InCursor); protected: GAMEINPUTBASE_API virtual bool ProcessInput(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual void ClearState(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual GameInputKind GetSupportedReadingKind() const override; GAMEINPUTBASE_API virtual bool CanProcessVirtualMouse() const; TSharedPtr Cursor; GameInputMouseState PreviousMouseState; FVector2D LastMouseOffset; enum { MaxSupportedButtons = 32 }; double RepeatTime[MaxSupportedButtons]; uint32 LastButtonHeldMask; }; /** * Processor for touch inputs */ class FGameInputTouchDeviceProcessor : public IGameInputDeviceProcessor { protected: GAMEINPUTBASE_API virtual bool ProcessInput(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual void ClearState(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual GameInputKind GetSupportedReadingKind() const override; struct FTouchData { FTouchData() { TouchId = INDEX_NONE; Position = FVector2D::ZeroVector; Pressure = 0.0f; bHasMoved = false; bIsActive = false; } int64 TouchId; FVector2D Position; float Pressure; bool bHasMoved; bool bIsActive; }; TArray PreviousTouchData; int32 ActiveTouchPoints = 0; // The max touch X and Y of the touch region to let us scale input based on the size of the touch area int32 MaxTouchX = 1920; int32 MaxTouchY = 1080; }; struct FGameInputDeviceConfiguration; struct FGameInputRawDeviceReportData; /** * Processor for raw input types (GameInputKindRawDeviceReport) * * Raw input is very customizable and is very different per-input device, so it requires a lot of configuration. * Most of the time it is special analog inputs on custom third party devices. A good example is the Whammy bar on * a guitar controller, or maybe some special shifter on a racing wheel that the Game Input API does not pick up by default. */ class FGameInputRawDeviceProcessor : public IGameInputDeviceProcessor { protected: GAMEINPUTBASE_API virtual bool ProcessInput(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual bool PostProcessInput(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual void ClearState(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual GameInputKind GetSupportedReadingKind() const override; /** * Reads the current state of the raw input buffer and populates CurrentRawData and PreviousRawData */ GAMEINPUTBASE_API const GameInputRawDeviceReportInfo* ReadCurrentRawInputState(const FGameInputEventParams& Params, IGameInputReading* ReadingToUse); /** * Translates the given uint8 RawValue to a float between 0.0 and +1.0, like most gamepad analog "triggers" * * @param RawValue * * @return A float between 0.0 and +1.0 */ GAMEINPUTBASE_API constexpr float RawValueToFloatTrigger(const uint8 RawValue) const; /** * Translates the given uint8 RawValue to a float between -1.0 and +1.0. * * @param RawValue * @param Deadzone * * @return A float between -1.0 and +1.0. Will be 0.0 if the value is within the deadzone. */ GAMEINPUTBASE_API const float RawValueToFloatAnalog(uint8 RawValue, const uint8 DeadZone = 2) const; /** * Processes all the current raw input data. This should only be called after we have read our current raw input report * or we have manually set the values (i.e. when we clear the input) */ GAMEINPUTBASE_API bool ProcessAllRawValues(const FGameInputEventParams& Params, const FGameInputDeviceConfiguration* DeviceConfig, const bool bShouldProcessButtons, const bool bShouldProcessAnalog); /** * Processes the raw value at the given index as a button bitmask. * For every bit in the raw value and treat it as 0 for pressed and 1 for on. */ GAMEINPUTBASE_API bool ProcessRawInputValueAsBitmask(const FGameInputEventParams& Params, const int32 RawValueIndex, const FGameInputRawDeviceReportData* AxisData); /** * Processes the raw value at the given index as a button value. If the value is non-zero, then treat it as pressed. * If the value is zero, treat it as unpressed. */ GAMEINPUTBASE_API bool ProcessRawInputValueAsButton(const FGameInputEventParams& Params, const int32 RawValueIndex, const FGameInputRawDeviceReportData* AxisData); /** * Processes the raw value at the given index as an analog value. This will translate the uint8 to a float value * and call OnControllerAnalog for it. */ GAMEINPUTBASE_API bool ProcessRawInputValueAsAanalog(const FGameInputEventParams& Params, const int32 RawValueIndex, const FGameInputRawDeviceReportData* AxisData); /** * Processes a pair of raw input indexes. This allows you to combine the values of multiple uint8 axis readings and combine them */ GAMEINPUTBASE_API bool ProcessRawInputValueAsAanalogPaired(const FGameInputEventParams& Params, const FGameInputRawDeviceReportData* AxisData); /** * The current raw input data that is populated by IGameInputRawDeviceReport::GetRawData */ TArray CurrentRawData; /** * The previous frame's raw input data that is set at the end of ProcessInput. */ TArray PreviousRawData; /** A struct that contains info about each raw input index */ struct FPerRawInputIndexData { // The max number of buttons possibly associated with this raw input data. The raw input data is // a uint8, so we can have at maximum a button per-bit, so 8. enum { MaxSupportedButtons = 8 }; // Stores the times that each button index has been pressed so we can detect key repeats double RepeatTime[MaxSupportedButtons]; /** * A map of key names to int values that is generated dynamically based on the button mapping. */ TMap KeyNameMap; }; /** * A map of an index to some raw data associated with it. * This is only used for button evaluation, not analog. */ TMap RawInputIndexDataMap; /** * Keeps track of how many times this gamepad has been processed this frame. * Every successful processing of button input in ProcessInput will increment this value. * This will get reset at the end of the input frame in PostProcessInput. */ int32 NumReadingsProcessedThisFrame = 0; }; /** * Processor for racing wheel types (GameInputKindRacingWheel) * * Note: Racing wheels are often paired with other device processors like Gamepad/Controller * to handle any "normal" buttons that may be on them. */ class FGameInputRacingWheelProcessor : public IGameInputDeviceProcessor { public: GAMEINPUTBASE_API FGameInputRacingWheelProcessor(); protected: GAMEINPUTBASE_API virtual bool ProcessInput(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual bool PostProcessInput(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual void ClearState(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual GameInputKind GetSupportedReadingKind() const override; /** * Returns the deadzone that we would like to use for racing wheel analog inputs. */ static GAMEINPUTBASE_API const float GetRacingWheelDeadzone(); /** * Processes the analog inputs of the wheel state (brake/clutch pedals, wheel movement, etc). */ GAMEINPUTBASE_API bool ProcessWheelAnalogState(const FGameInputEventParams& Params, GameInputRacingWheelState& CurrentWheelState); /** * Processes any buttons on the racing wheel (next/previous gears, menu nav, etc); */ GAMEINPUTBASE_API bool ProcessWheelButtonState(const FGameInputEventParams& Params, GameInputRacingWheelState& CurrentWheelState); /** * Keeps track of how many times this gamepad has been processed this frame. * Every successful processing of button input in ProcessInput will increment this value. * This will get reset at the end of the input frame in PostProcessInput. */ int32 NumReadingsProcessedThisFrame = 0; /** The previous game input state that was last processed. */ GameInputRacingWheelState PreviousState; /** * Array of repeat times to calculate if a button has been held long enough * to receive an IE_REPEAT event. */ static const uint32 MaxSupportedButtons = 16; double RepeatTime[MaxSupportedButtons]; }; /** * Processor for the GameInputKindArcadeStick type. * * These are typically accessories to "fighting" style games, or other arcade style games. */ class FGameInputArcadeStickProcessor : public IGameInputDeviceProcessor { public: GAMEINPUTBASE_API FGameInputArcadeStickProcessor(); protected: GAMEINPUTBASE_API virtual bool ProcessInput(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual void ClearState(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual GameInputKind GetSupportedReadingKind() const override; /** The previously processed arcade stick state. */ GameInputArcadeStickState PreviousState; /** * Array of repeat times to calculate if a button has been held long enough * to receive an IE_REPEAT event. */ static const uint32 MaxSupportedButtons = 16; double RepeatTime[MaxSupportedButtons]; }; /** * Processor for the GameInputKindFlightStick type. */ class FGameInputFlightStickProcessor : public IGameInputDeviceProcessor { public: GAMEINPUTBASE_API FGameInputFlightStickProcessor(); protected: GAMEINPUTBASE_API virtual bool ProcessInput(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual bool PostProcessInput(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual void ClearState(const FGameInputEventParams& Params) override; GAMEINPUTBASE_API virtual GameInputKind GetSupportedReadingKind() const override; /** * Process Flight stick specific buttons. */ GAMEINPUTBASE_API bool ProcessFlightStickButtons(const FGameInputEventParams& Params, GameInputFlightStickState& State); /** * Process the analog values (yaw, pitch, roll, and throttle) of the flight stick */ GAMEINPUTBASE_API bool ProcessFlightStickAnalog(const FGameInputEventParams& Params, GameInputFlightStickState& State); /** * Keeps track of how many times this gamepad has been processed this frame. * Every successful processing of button input in ProcessInput will increment this value. * This will get reset at the end of the input frame in PostProcessInput. */ int32 NumReadingsProcessedThisFrame = 0; /** State of the flight stick from the previous frame */ GameInputFlightStickState PreviousState = {}; // Repeat times for any pressed buttons static constexpr uint32 MaxSupportedButtons = 32; double RepeatTime[MaxSupportedButtons]; /** Repeat times for when switches are pressed (aka the DPad) */ TArray SwitchRepeatTimes; }; #endif // GAME_INPUT_SUPPORT