Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,65 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
class InputChannel;
////////////////////////////////////////////////////////////////////////////////////////////////
//! EBus interface used to listen for input events as they're broadcast from input channels when
//! they are active or when their state/value changes. Most common input consumers should derive
//! instead from InputChannelEventListener (which respects the 'o_hasBeenConsumed' parameter for
//! OnInputChannelEvent) to ensure events are only processed once. However, if a system needs to
//! process input events that may have already been consumed by a higher priority listener, they
//! are free to derive from InputChannelNotificationBus::Handler and ignore 'o_hasBeenConsumed'.
class InputChannelNotifications : public AZ::EBusTraits
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: input notifications are addressed to a single address
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: input notifications can be handled by multiple (ordered) listeners
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::MultipleAndOrdered;
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
virtual ~InputChannelNotifications() = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Override to be notified when an input channel is active or its state or value is updated
//! \param[in] inputChannel The input channel that is active or whose state or value updated
//! \param[in,out] o_hasBeenConsumed Check and/or set whether the event has been handled
virtual void OnInputChannelEvent(const InputChannel& /*inputChannel*/,
bool& /*o_hasBeenConsumed*/) {}
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the priority of the input notification handler (sorted from highest to lowest)
//! \return Priority of the input notification handler
virtual AZ::s32 GetPriority() const { return 0; }
////////////////////////////////////////////////////////////////////////////////////////////
//! Compare function required by BusHandlerOrderCompare = BusHandlerCompareDefault
//! \param[in] other Another instance of the class to compare
//! \return True if the priority of this handler is greater than the other, false otherwise
inline bool Compare(const InputChannelNotifications* other) const
{
return GetPriority() > other->GetPriority();
}
};
using InputChannelNotificationBus = AZ::EBus<InputChannelNotifications>;
} // namespace AzFramework
@@ -0,0 +1,66 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
class InputDevice;
////////////////////////////////////////////////////////////////////////////////////////////////
//! EBus interface used to listen for input events as they're broadcast from input devices when
//! they connect or disconnect from the system. Some common input devices are assumed to always
//! be connected, and will never generate these notifications. This interface could be extended
//! to include notifications for other events related to input devices, for example low battery.
class InputDeviceNotifications : public AZ::EBusTraits
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: input notifications are addressed to a single address
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: input notifications can be handled by multiple (ordered) listeners
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::MultipleAndOrdered;
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
virtual ~InputDeviceNotifications() = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Override to be notified when input devices connect to the system
//! \param[in] inputChannel The input device that connected
virtual void OnInputDeviceConnectedEvent(const InputDevice& /*inputDevice*/) {}
////////////////////////////////////////////////////////////////////////////////////////////
//! Override to be notified when input devices disconnect from the system
//! \param[in] inputChannel The input device that disconnected
virtual void OnInputDeviceDisconnectedEvent(const InputDevice& /*inputDevice*/) {}
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the priority of the input notification handler (sorted from highest to lowest)
//! \return Priority of the input notification handler
virtual AZ::s32 GetPriority() const { return 0; }
////////////////////////////////////////////////////////////////////////////////////////////
//! Compare function required by BusHandlerOrderCompare = BusHandlerCompareDefault
//! \param[in] other Another instance of the class to compare
//! \return True if the priority of this handler is greater than the other, false otherwise
inline bool Compare(const InputDeviceNotifications* other) const
{
return GetPriority() > other->GetPriority();
}
};
using InputDeviceNotificationBus = AZ::EBus<InputDeviceNotifications>;
} // namespace AzFramework
@@ -0,0 +1,45 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! EBus interface used to listen for input system notifications
class InputSystemNotifications : public AZ::EBusTraits
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: input notifications are addressed to a single address
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: input notifications can be handled by multiple listeners
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
virtual ~InputSystemNotifications() = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Override to be notified right before the input system is about to tick input devices
virtual void OnPreInputUpdate() {}
////////////////////////////////////////////////////////////////////////////////////////////
//! Override to be notified right after the input system is about to tick input devices
virtual void OnPostInputUpdate() {}
};
using InputSystemNotificationBus = AZ::EBus<InputSystemNotifications>;
} // namespace AzFramework
@@ -0,0 +1,64 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/string/string.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! EBus interface used to listen for input text events that are broadcast from input devices or
//! channels. Most text input consumers should derive instead from InputTextEventListener (which
//! respects the 'o_hasBeenConsumed' parameter passed to OnInputTextEvent) to ensure text events
//! are processed once. However, if for some reason a system needs to process text regardless of
//! whether it has been consumed by a higher priority listener, they are free to derive directly
//! from InputTextEventNotificationsBus::Handler and simply ignore the 'o_hasBeenConsumed' flag.
class InputTextNotifications : public AZ::EBusTraits
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: input notifications are addressed to a single address
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: input notifications can be handled by multiple (ordered) listeners
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::MultipleAndOrdered;
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
virtual ~InputTextNotifications() = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Override to be notified when unicode text input is generated by an input device
//! \param[in] textUTF8 The text emitted by the input device (encoded using UTF-8)
//! \param[in,out] o_hasBeenConsumed Check and/or set whether the text has been handled
virtual void OnInputTextEvent(const AZStd::string& /*textUTF8*/,
bool& /*o_hasBeenConsumed*/) {}
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the priority of the input notification handler (sorted from highest to lowest)
//! \return Priority of the input notification handler
virtual AZ::s32 GetPriority() const { return 0; }
////////////////////////////////////////////////////////////////////////////////////////////
//! Compare function required by BusHandlerOrderCompare = BusHandlerCompareDefault
//! \param[in] other Another instance of the class to compare
//! \return True if the priority of this handler is greater than the other, false otherwise
inline bool Compare(const InputTextNotifications* other) const
{
return GetPriority() > other->GetPriority();
}
};
using InputTextNotificationBus = AZ::EBus<InputTextNotifications>;
} // namespace AzFramework
@@ -0,0 +1,242 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Channels/InputChannelId.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/RTTI/ReflectContext.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
class InputChannel;
////////////////////////////////////////////////////////////////////////////////////////////////
//! EBus interface used to query for available input channels
class InputChannelRequests : public AZ::EBusTraits
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests can be addressed to a specific InputChannelId so that they are only
//! handled by one input channel that has connected to the bus using that unique id, or they
//! can be broadcast to all input channels that have connected to the bus, regardless of id.
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests should be handled by only one input channel connected to each id
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests should be addressed to a specific channel id / device index pair.
//! While input channel ids must be unique across different input devices, multiple devices
//! of the same type can exist, so requests must be addressed using an id/device index pair.
class BusIdType
{
public:
////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(BusIdType, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_TYPE_INFO(BusIdType, "{FA0B740B-8917-4260-B402-05444C985AB5}");
////////////////////////////////////////////////////////////////////////////////////////
// Reflection
static void Reflect(AZ::ReflectContext* context);
////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] channelId Id of the input channel to address requests
//! \param[in] deviceIndex Index of the input device to address requests
BusIdType(const InputChannelId& channelId, AZ::u32 deviceIndex = 0)
: m_channelId(channelId)
, m_deviceIndex(deviceIndex)
{}
////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] channelName Name of the input channel to address requests
//! \param[in] deviceIndex Index of the input device to address requests
BusIdType(const char* channelName, AZ::u32 deviceIndex = 0)
: m_channelId(channelName)
, m_deviceIndex(deviceIndex)
{}
////////////////////////////////////////////////////////////////////////////////////////
///@{
//! Equality comparison operator
//! \param[in] other Another instance of the class to compare for equality
bool operator==(const BusIdType& other) const;
bool operator!=(const BusIdType& other) const;
///@}
////////////////////////////////////////////////////////////////////////////////////////
// Variables
InputChannelId m_channelId; //!< Id of the input channel to address requests
AZ::u32 m_deviceIndex; //!< Index of the input device to address requests
};
////////////////////////////////////////////////////////////////////////////////////////////
//! Finds a specific input channel given its id and the index of the device that owns it.
//! This convenience function wraps an EBus call to InputChannelRequests::GetInputChannel.
//! \param[in] channelId Id of the input channel to find
//! \param[in] deviceIndex Index of the device that owns the input channel
//! \return Pointer to the input channel if it was found, nullptr if it was not
static const InputChannel* FindInputChannel(const InputChannelId& channelId,
AZ::u32 deviceIndex = 0);
////////////////////////////////////////////////////////////////////////////////////////////
//! Returns the input channel uniquely identified by the id/index pair used to address calls.
//! This should never be broadcast otherwise the channel returned will effectively be random.
//!
//! Examples:
//!
//! // Get the left mouse button input channel
//! const InputChannelRequests::BusIdType requestId(InputDeviceMouse::Button::Left);
//! const InputChannel* inputChannel = nullptr;
//! InputChannelRequestBus::EventResult(inputChannel,
//! requestId,
//! &InputChannelRequests::GetInputChannel);
//!
//! // Get the A button input channel for the gamepad device at index 2
//! const InputChannelRequests::BusIdType requestId(InputDeviceGamepad::Button::A, 2);
//! const InputChannel* inputChannel = nullptr;
//! InputChannelRequestBus::EventResult(inputChannel,
//! requestId,
//! &InputChannelRequests::GetInputChannel);
//!
//! \return Pointer to the input channel if it exists, nullptr otherwise
virtual const InputChannel* GetInputChannel() const = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Reset the channel's state, which should broadcast an 'Ended' input notification event
//! (if the channel is currently active) before returning the channel to the idle state.
virtual void ResetState() = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Simulate a raw input event. Please use with caution; it's designed primarily for testing
//! purposes, and could result in strange behaviour if called while the user is interacting
//! physically with any input device that happens to be updating the same input channel(s),
//! or if used to simulate input of an input channel whose value is derived from the value
//! of a different input channel (eg. the InputDeviceGamepad::ThumbStickDirection::* input
//! channel values are derived from their respective InputDeviceGamepad::ThumbStickAxis2D).
//!
//! If used, it is the responsibility of the caller to reset the input channel back to it's
//! original idle state, otherwise it may be left in a state of being permanently 'active'.
//!
//! \param[in] rawValue The raw input value to simulate. Analog input channels will use the
//! value directly, digital input channels treat 0.0f as 'off' and all other values as 'on'.
virtual void SimulateRawInput(float /*rawValue*/) {}
////////////////////////////////////////////////////////////////////////////////////////////
//! Simulate a raw input event. Please use with caution; it's designed primarily for testing
//! purposes, and could result in strange behaviour if called while the user is interacting
//! physically with any input device that happens to be updating the same input channel(s),
//! or if used to simulate input of an input channel whose value is derived from the value
//! of a different input channel (eg. the InputDeviceGamepad::ThumbStickDirection::* input
//! channel values are derived from their respective InputDeviceGamepad::ThumbStickAxis2D).
//!
//! If used, it is the responsibility of the caller to reset the input channel back to it's
//! original idle state, otherwise it may be left in a state of being permanently 'active'.
//!
//! \param[in] rawValueX The raw x-axis input value to simulate.
//! \param[in] rawValueY The raw y-axis input value to simulate.
virtual void SimulateRawInput2D(float /*rawValueX*/,
float /*rawValueY*/) {}
////////////////////////////////////////////////////////////////////////////////////////////
//! Simulate a raw input event. Please use with caution; it's designed primarily for testing
//! purposes, and could result in strange behaviour if called while the user is interacting
//! physically with any input device that happens to be updating the same input channel(s),
//! or if used to simulate input of an input channel whose value is derived from the value
//! of a different input channel (eg. the InputDeviceGamepad::ThumbStickDirection::* input
//! channel values are derived from their respective InputDeviceGamepad::ThumbStickAxis2D).
//!
//! If used, it is the responsibility of the caller to reset the input channel back to it's
//! original idle state, otherwise it may be left in a state of being permanently 'active'.
//!
//! \param[in] rawValueX The raw x-axis input value to simulate.
//! \param[in] rawValueY The raw y-axis input value to simulate.
//! \param[in] rawValueZ The raw z-axis input value to simulate.
virtual void SimulateRawInput3D(float /*rawValueX*/,
float /*rawValueY*/,
float /*rawValueZ*/) {}
////////////////////////////////////////////////////////////////////////////////////////////
//! Simulate a raw input event. Please use with caution; it's designed primarily for testing
//! purposes, and could result in strange behaviour if called while the user is interacting
//! physically with any input device that happens to be updating the same input channel(s),
//! or if used to simulate input of an input channel whose value is derived from the value
//! of a different input channel (eg. the InputDeviceGamepad::ThumbStickDirection::* input
//! channel values are derived from their respective InputDeviceGamepad::ThumbStickAxis2D).
//!
//! If used, it is the responsibility of the caller to reset the input channel back to it's
//! original idle state, otherwise it may be left in a state of being permanently 'active'.
//!
//! \param[in] rawValue The raw input value to simulate. Analog input channels will use the
//! value directly, digital input channels treat 0.0f as 'off' and all other values as 'on'.
//! \param[in] normalizedX The normalized x position of the simulated raw input event.
//! \param[in] normalizedY The normalized y position of the simulated raw input event.
virtual void SimulateRawInputWithPosition2D(float /*rawValue*/,
float /*normalizedX*/,
float /*normalizedY*/) {}
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
virtual ~InputChannelRequests() = default;
};
using InputChannelRequestBus = AZ::EBus<InputChannelRequests>;
////////////////////////////////////////////////////////////////////////////////////////////////
inline bool InputChannelRequests::BusIdType::operator==(const InputChannelRequests::BusIdType& other) const
{
return (m_channelId == other.m_channelId) && (m_deviceIndex == other.m_deviceIndex);
}
////////////////////////////////////////////////////////////////////////////////////////////////
inline bool InputChannelRequests::BusIdType::operator!=(const BusIdType& other) const
{
return !(*this == other);
}
////////////////////////////////////////////////////////////////////////////////////////////////
inline const InputChannel* InputChannelRequests::FindInputChannel(const InputChannelId& channelId,
AZ::u32 deviceIndex)
{
const InputChannel* inputChannel = nullptr;
const BusIdType inputChannelRequestId(channelId, deviceIndex);
InputChannelRequestBus::EventResult(inputChannel,
inputChannelRequestId,
&InputChannelRequests::GetInputChannel);
return inputChannel;
}
} // namespace AzFramework
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AZStd
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Hash structure specialization for InputChannelRequests::BusIdType
template<> struct hash<AzFramework::InputChannelRequests::BusIdType>
{
inline size_t operator()(const AzFramework::InputChannelRequests::BusIdType& busIdType) const
{
size_t hashValue = busIdType.m_channelId.GetNameCrc32();
AZStd::hash_combine(hashValue, busIdType.m_deviceIndex);
return hashValue;
}
};
} // namespace AZStd
@@ -0,0 +1,273 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Channels/InputChannel.h>
#include <AzFramework/Input/Devices/InputDeviceId.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/EBus/EBus.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
class InputDevice;
////////////////////////////////////////////////////////////////////////////////////////////////
//! EBus interface used to query input devices for their associated input channels and state
class InputDeviceRequests : public AZ::EBusTraits
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests can be addressed to a specific InputDeviceId so that they are only
//! handled by one input device that has connected to the bus using that unique id, or they
//! can be broadcast to all input devices that have connected to the bus, regardless of id.
//! Connected input devices are ordered by their local player index from lowest to highest.
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ByIdAndOrdered;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests should be handled by only one input device connected to each id
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests can be addressed to a specific InputDeviceId
using BusIdType = InputDeviceId;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests are handled by connected devices in the order of local player index
using BusIdOrderCompare = AZStd::less<BusIdType>;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: InputDeviceRequestBus can be accessed from multiple threads, but is safe to use with
//! LocklessDispatch because connect/disconnect is handled only on engine startup/shutdown (InputSystemComponent).
using MutexType = AZStd::recursive_mutex;
static const bool LocklessDispatch = true;
////////////////////////////////////////////////////////////////////////////////////////////
///@{
//! Alias for verbose container class
using InputDeviceIdSet = AZStd::unordered_set<InputDeviceId>;
using InputChannelIdSet = AZStd::unordered_set<InputChannelId>;
using InputDeviceByIdMap = AZStd::unordered_map<InputDeviceId, const InputDevice*>;
using InputChannelByIdMap = AZStd::unordered_map<InputChannelId, const InputChannel*>;
///@}
////////////////////////////////////////////////////////////////////////////////////////////
//! Finds a specific input device (convenience function)
//! \param[in] deviceId Id of the input device to find
//! \return Pointer to the input device if it was found, nullptr if it was not
static const InputDevice* FindInputDevice(const InputDeviceId& deviceId);
////////////////////////////////////////////////////////////////////////////////////////////
//! Request the ids of all input channels (optionally those associated with an input device)
//! that return custom data of a specific type (InputChannel::GetCustomData<CustomDataType>).
//! \param[out] o_channelIds The set of input channel ids to return
//! \param[in] deviceId (optional) Id of a specific input device to query for input channels
//! \tparam CustomDataType Only consider input channels that return custom data of this type
template<class CustomDataType>
static void GetInputChannelIdsWithCustomDataOfType(InputChannelIdSet& o_channelIds,
const InputDeviceId* deviceId = nullptr);
////////////////////////////////////////////////////////////////////////////////////////////
//! Gets the input device that is uniquely identified by the InputDeviceId used to address
//! the call to this EBus function. Calls to this EBus method should never be broadcast to
//! all connected input devices, otherwise the device returned will effectively be random.
//! \return Pointer to the input device if it exists, nullptr otherwise
virtual const InputDevice* GetInputDevice() const = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Request the ids of all currently enabled input devices. This does not imply they are all
//! connected, or even available on the current platform, just that they are enabled for the
//! application (meaning they will generate input when available / connected to the system).
//!
//! Can be called using either:
//! - EBus<>::Broadcast (all input devices will add their id to o_deviceIds)
//! - EBus<>::Event(id) (the given device will add its id to o_deviceIds - not very useful!)
//!
//! \param[out] o_deviceIds The set of input device ids to return
virtual void GetInputDeviceIds(InputDeviceIdSet& o_deviceIds) const = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Request a map of all currently enabled input devices by id. This does not imply they are
//! connected, or even available on the current platform, just that they are enabled for the
//! application (meaning they will generate input when available / connected to the system).
//!
//! Can be called using either:
//! - EBus<>::Broadcast (all input devices will add themselves to o_devicesById)
//! - EBus<>::Event(id) (the given input device will add itself to o_devicesById)
//!
//! \param[out] o_devicesById The map of input devices (keyed by their id) to return
virtual void GetInputDevicesById(InputDeviceByIdMap& o_devicesById) const = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Request a map of all currently enabled input devices (keyed by their id) that have been
//! assigned to the specified local user id.
//!
//! Can be called using either:
//! - EBus<>::Broadcast (all input devices assigned to localUserId added to o_devicesById)
//! - EBus<>::Event(id) (add given input device to o_devicesById if assigned to localUserId)
//!
//! \param[out] o_devicesById The map of input devices (keyed by their id) to return
//! \param[in] localUserId The local user id to check whether input devices are assigned to
virtual void GetInputDevicesByIdWithAssignedLocalUserId(InputDeviceByIdMap& o_devicesById,
LocalUserId localUserId) const = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Request the ids of all input channels associated with an input device.
//!
//! Can be called using either:
//! - EBus<>::Broadcast (all input devices will add all their channel ids to o_channelIds)
//! - EBus<>::Event(id) (the given device will add all of its channel ids to o_channelIds)
//!
//! \param[out] o_channelIds The set of input channel ids to return
virtual void GetInputChannelIds(InputChannelIdSet& o_channelIds) const = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Request all input channels associated with an input device.
//!
//! Can be called using either:
//! - EBus<>::Broadcast (all input devices will add all their channels to o_channelsById)
//! - EBus<>::Event(id) (the given device will add all of its channels to o_channelsById)
//!
//! \param[out] o_channelsById The map of input channels (keyed by their id) to return
virtual void GetInputChannelsById(InputChannelByIdMap& o_channelsById) const = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Request the text displayed on the physical key / button associated with an input channel.
//! In the case of keyboard keys, this should take into account the current keyboard layout.
//!
//! Can be called using either:
//! - EBus<>::Broadcast (all input devices will search their channels for inputChannelId)
//! - EBus<>::Event(id) (the given device will search its channel for inputChannelId)
//!
//! \param[in] inputChannelId The input channel id whose key or button text to search for
//! \param[out] o_keyOrButtonText The text displayed on the physical key or button if found
virtual void GetPhysicalKeyOrButtonText(const InputChannelId& /*inputChannelId*/,
AZStd::string& /*o_keyOrButtonText*/) const {}
////////////////////////////////////////////////////////////////////////////////////////////
//! Tick/update input devices.
//!
//! Can be called using either:
//! - EBus<>::Broadcast (all input devices are ticked/updated)
//! - EBus<>::Event(id) (the given device is ticked/updated)
virtual void TickInputDevice() = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
virtual ~InputDeviceRequests() = default;
};
using InputDeviceRequestBus = AZ::EBus<InputDeviceRequests>;
////////////////////////////////////////////////////////////////////////////////////////////////
inline const InputDevice* InputDeviceRequests::FindInputDevice(const InputDeviceId& deviceId)
{
const InputDevice* inputDevice = nullptr;
InputDeviceRequestBus::EventResult(inputDevice, deviceId, &InputDeviceRequests::GetInputDevice);
return inputDevice;
}
////////////////////////////////////////////////////////////////////////////////////////////////
template<class CustomDataType>
inline void InputDeviceRequests::GetInputChannelIdsWithCustomDataOfType(
InputChannelIdSet& o_channelIds,
const InputDeviceId* deviceId)
{
InputChannelByIdMap inputChannelsById;
if (deviceId)
{
InputDeviceRequestBus::Event(*deviceId, &InputDeviceRequests::GetInputChannelsById, inputChannelsById);
}
else
{
InputDeviceRequestBus::Broadcast(&InputDeviceRequests::GetInputChannelsById, inputChannelsById);
}
for (const auto& inputChannelById : inputChannelsById)
{
if (inputChannelById.second->GetCustomData<CustomDataType>() != nullptr)
{
o_channelIds.insert(inputChannelById.first);
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
//! Templated EBus interface used to create a custom implementation for a specific device type
template<class InputDeviceType>
class InputDeviceImplementationRequest : public AZ::EBusTraits
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Alias for the EBus implementation of this interface
using Bus = AZ::EBus<InputDeviceImplementationRequest<InputDeviceType>>;
////////////////////////////////////////////////////////////////////////////////////////////
//! Alias for the function type used to create the custom implementations
using CreateFunctionType = typename InputDeviceType::Implementation*(*)(InputDeviceType&);
////////////////////////////////////////////////////////////////////////////////////////////
//! Create a custom implementation for all the existing instances of this input device type.
//! Passing InputDeviceType::Implementation::Create as the argument will create the default
//! device implementation, while passing nullptr will delete any existing implementation.
//! \param[in] createFunction Pointer to the function that will create the implementation.
virtual void CreateCustomImplementation(CreateFunctionType createFunction) = 0;
};
////////////////////////////////////////////////////////////////////////////////////////////////
//! Templated EBus handler class that implements the InputDeviceImplementationRequest interface.
//! To use this helper class your InputDeviceType class must posses all of the following traits,
//! and they must all be accessible (either by being public or by making this helper a friend):
//! - A nested InputDeviceType::Implementation class
//! - A SetImplementation(AZStd::unique_ptr<InputDeviceType::Implementation>) function
template<class InputDeviceType>
class InputDeviceImplementationRequestHandler
: public InputDeviceImplementationRequest<InputDeviceType>::Bus::Handler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputDevice Reference to the input device that owns this handler
AZ_INLINE InputDeviceImplementationRequestHandler(InputDeviceType& inputDevice)
: m_inputDevice(inputDevice)
{
InputDeviceImplementationRequest<InputDeviceType>::Bus::Handler::BusConnect();
}
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputDeviceImplementationRequestHandler);
protected:
////////////////////////////////////////////////////////////////////////////////////////////
//! Alias for the function type used to create the custom implementations
using CreateFunctionType = typename InputDeviceType::Implementation*(*)(InputDeviceType&);
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref InputDeviceImplementationRequest<InputDeviceType>::CreateCustomImplementation
AZ_INLINE void CreateCustomImplementation(CreateFunctionType createFunction) override
{
AZStd::unique_ptr<typename InputDeviceType::Implementation> newImplementation;
if (createFunction)
{
newImplementation.reset(createFunction(m_inputDevice));
}
m_inputDevice.SetImplementation(AZStd::move(newImplementation));
}
private:
InputDeviceType& m_inputDevice; //!< Reference to the input device that owns this handler
};
} // namespace AzFramework
@@ -0,0 +1,61 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Devices/InputDeviceId.h>
#include <AzCore/EBus/EBus.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! EBus interface used to send haptic feedback requests to connected input devices
class InputHapticFeedbackRequests : public AZ::EBusTraits
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests can be addressed to a specific InputDeviceId using EBus<>::Event,
//! which should be handled by only one device that has connected to the bus using that id.
//! Input requests can also be sent using EBus<>::Broadcast, in which case they'll be sent
//! to all input devices that have connected to the input event bus regardless of their id.
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests should be handled by only one input device connected to each id
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests can be addressed to a specific InputDeviceId
using BusIdType = InputDeviceId;
////////////////////////////////////////////////////////////////////////////////////////////
//! Set the current vibration (force-feedback) of the input device. All calls to this method
//! should be addressed to a specific input device, otherwise all devices that support force
//! feedback will respond! To stop all vibration, call this passing 0.0f for both parameters.
//!
//! Called using either:
//! - EBus<>::Broadcast (any input device can respond to the request)
//! - EBus<>::Event(id) (the given device can respond to the request)
//!
//! \param[in] leftMotorSpeedNormalized Speed of the left (large/low frequency) motor
//! \param[in] rightMotorSpeedNormalized Speed of the right (small/high frequency) motor
virtual void SetVibration(float /*leftMotorSpeedNormalized*/,
float /*rightMotorSpeedNormalized*/) = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
virtual ~InputHapticFeedbackRequests() = default;
};
using InputHapticFeedbackRequestBus = AZ::EBus<InputHapticFeedbackRequests>;
} // namespace AzFramework
@@ -0,0 +1,68 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Devices/InputDeviceId.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Color.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! EBus interface used to send light bar requests to connected input devices
class InputLightBarRequests : public AZ::EBusTraits
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests can be addressed to a specific InputDeviceId using EBus<>::Event,
//! which should be handled by only one device that has connected to the bus using that id.
//! Input requests can also be sent using EBus<>::Broadcast, in which case they'll be sent
//! to all input devices that have connected to the input event bus regardless of their id.
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests should be handled by only one input device connected to each id
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests can be addressed to a specific InputDeviceId
using BusIdType = InputDeviceId;
////////////////////////////////////////////////////////////////////////////////////////////
//! Set the current light bar color of the input device. All calls to this method should be
//! addressed to a specific input device otherwise all devices that support it will respond!
//!
//! Called using either:
//! - EBus<>::Broadcast (any input device can respond to the request)
//! - EBus<>::Event(id) (the given device can respond to the request)
//!
//! \param[in] color The color to set the light bar
virtual void SetLightBarColor(const AZ::Color& color) = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Reset to default light bar color of the input device. All calls to this method should be
//! addressed to a specific input device otherwise all devices that support it will respond!
//!
//! Called using either:
//! - EBus<>::Broadcast (any input device can respond to the request)
//! - EBus<>::Event(id) (the given device can respond to the request)
virtual void ResetLightBarColor() = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
virtual ~InputLightBarRequests() = default;
};
using InputLightBarRequestBus = AZ::EBus<InputLightBarRequests>;
} // namespace AzFramework
@@ -0,0 +1,74 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Channels/InputChannelId.h>
#include <AzFramework/Input/Devices/InputDeviceId.h>
#include <AzCore/EBus/EBus.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! EBus interface used to send motion sensor requests to connected input devices
class InputMotionSensorRequests : public AZ::EBusTraits
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests can be addressed to a specific InputDeviceId using EBus<>::Event,
//! which should be handled by only one device that has connected to the bus using that id.
//! Input requests can also be sent using EBus<>::Broadcast, in which case they'll be sent
//! to all input devices that have connected to the input event bus regardless of their id.
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests should be handled by only one input device connected to each id
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests can be addressed to a specific InputDeviceId
using BusIdType = InputDeviceId;
////////////////////////////////////////////////////////////////////////////////////////////
//! Get the enabled state of a specific input channel. The majority of input channels cannot
//! be disabled and are enabled by default, but motion sensor input can be explicitly turned
//! on/off in order to preserve battery and so as to not generate a flood of unneeded events.
//!
//! Called using either:
//! - EBus<>::Broadcast (any input device can respond to the request)
//! - EBus<>::Event(id) (the given device can respond to the request)
//!
//! \param[in] channelId Id of the input channel to check whether it is enabled or diabled
//! \return True if the input channel is currently enabled, false otherwise
virtual bool GetInputChannelEnabled(const InputChannelId& /*channelId*/) = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Set the enabled state of a specific input channel. The majority of input channels cannot
//! be disabled and are enabled by default, but motion sensor input can be explicitly turned
//! on/off in order to preserve battery and so as to not generate a flood of unneeded events.
//!
//! Called using either:
//! - EBus<>::Broadcast (any input device can respond to the request)
//! - EBus<>::Event(id) (the given device can respond to the request)
//!
//! \param[in] channelId Id of the input channel to be enabled or diabled
//! \param[in] enabled Should the input channel be enabled (true) or disabled (false)?
virtual void SetInputChannelEnabled(const InputChannelId& /*channelId*/, bool /*enabled*/) = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
virtual ~InputMotionSensorRequests() = default;
};
using InputMotionSensorRequestBus = AZ::EBus<InputMotionSensorRequests>;
} // namespace AzFramework
@@ -0,0 +1,146 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Devices/InputDeviceId.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Vector2.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! State of the system cursor
enum class SystemCursorState
{
Unknown, //!< The state of the system cursor is not known
ConstrainedAndHidden, //!< Constrained to the application's main window and hidden
ConstrainedAndVisible, //!< Constrained to the application's main window and visible
UnconstrainedAndHidden, //!< Free to move outside the main window but hidden while inside
UnconstrainedAndVisible //!< Free to move outside the application's main window and visible
};
////////////////////////////////////////////////////////////////////////////////////////////////
//! EBus interface used to query/change the state, position, or appearance of the system cursor
class InputSystemCursorRequests : public AZ::EBusTraits
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests can be addressed to a specific InputDeviceId using EBus<>::Event,
//! which should be handled by only one device that has connected to the bus using that id.
//! Input requests can also be sent using EBus<>::Broadcast, in which case they'll be sent
//! to all input devices that have connected to the input event bus regardless of their id.
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests should be handled by only one input device connected to each id
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests can be addressed to a specific InputDeviceId
using BusIdType = InputDeviceId;
////////////////////////////////////////////////////////////////////////////////////////////
//! Inform input devices that the system cursor state should be changed. Calls to the method
//! should usually be addressed to a mouse input device, but it may be possible for multiple
//! different device instances to each be associated with a system cursor.
//!
//! Called using either:
//! - EBus<>::Broadcast (any input device can respond to the request)
//! - EBus<>::Event(id) (the given device can respond to the request)
//!
//! \param[in] systemCursorState The desired system cursor state
virtual void SetSystemCursorState(SystemCursorState /*systemCursorState*/) = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Query input devices for the current state of the system cursor. All calls to this method
//! should usually be addressed to a mouse input device, but it may be possible for multiple
//! different device instances to each be associated with a system cursor.
//!
//! Called using either:
//! - EBus<>::Broadcast (any input device can respond to the request)
//! - EBus<>::Event(id) (the given device can respond to the request)
//!
//! \return The current state of the system cursor
virtual SystemCursorState GetSystemCursorState() const = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Inform input devices that the system cursor position should be set. Calls to the method
//! should usually be addressed to a mouse input device, but it may be possible for multiple
//! different device instances to each be associated with a system cursor.
//!
//! Called using either:
//! - EBus<>::Broadcast (any input device can respond to the request)
//! - EBus<>::Event(id) (the given device can respond to the request)
//!
//! \param[in] positionNormalized The desired system cursor position normalized
virtual void SetSystemCursorPositionNormalized(AZ::Vector2 positionNormalized) = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Get the system cursor position normalized relative to the application's main window. The
//! position obtained has had os ballistics applied, and is valid regardless of whether the
//! system cursor is hidden or visible. When the cursor is constrained to the application's
//! main window the values will always be in the [0.0, 1.0] range, but if unconstrained the
//! normalized values will not be clamped so they will exceed this range anytime the system
//! cursor is located outside the application's main window.
//! See also InputSystemCursorRequests::SetSystemCursorState and GetSystemCursorState.
//!
//! Called using either:
//! - EBus<>::Broadcast (any input device can respond to the request)
//! - EBus<>::Event(id) (the given device can respond to the request)
//!
//! \return The system cursor position normalized relative to the application's main window
virtual AZ::Vector2 GetSystemCursorPositionNormalized() const = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
virtual ~InputSystemCursorRequests() = default;
};
using InputSystemCursorRequestBus = AZ::EBus<InputSystemCursorRequests>;
////////////////////////////////////////////////////////////////////////////////////////////////
//! EBus interface to request the window or view used to clip and/or normalize the system cursor
class InputSystemCursorConstraintRequests : public AZ::EBusTraits
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests are addressed to a single address
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests are handled by a single listener
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
virtual ~InputSystemCursorConstraintRequests() = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Get the application window/view that should be used to clip and/or normalize the cursor.
//!
//! Ideally there should be an abstract cross-platform 'AzFramework::ApplicationWindow/View'
//! class/interface that systems like input and rendering use to interact with the windowing
//! system in a platform agnostic manner, but for now we will make do with returning a void*
//! and depend on the caller to cast it to the appropriate platform specific representation.
//!
//! \return Pointer to a platform specific representation of the application window or view
//! that should be used to clip and/or normalize the system cursor. If nullptr is returned,
//! the default main window/view will be used instead. Return type depends on the platform,
//! as does the fallback main window/view that will be used instead if nullptr is returned:
//! - Windows: HWND (fallback ::GetFocus())
//! - macOS: NSView (fallback NSApplication.sharedApplication.mainWindow.contentView)
virtual void* GetSystemCursorConstraintWindow() const = 0;
};
using InputSystemCursorConstraintRequestBus = AZ::EBus<InputSystemCursorConstraintRequests>;
} // namespace AzFramework
@@ -0,0 +1,47 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! EBus interface used to send requests to the input system
class InputSystemRequests : public AZ::EBusTraits
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: the input system is a singleton, requests are addressed to a single address.
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: the input system is a singleton, requests are handled by a single listener.
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
////////////////////////////////////////////////////////////////////////////////////////////
//! Recreates all enabled input devices after destroying any that happen to already exist.
virtual void RecreateEnabledInputDevices() = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Tick/update the input system. This is called during the AZ::ComponentTickBus::TICK_INPUT
//! priority update of the AZ::TickBus, but can be called independently any time when needed.
virtual void TickInput() = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
virtual ~InputSystemRequests() = default;
};
using InputSystemRequestBus = AZ::EBus<InputSystemRequests>;
} // namespace AzFramework
@@ -0,0 +1,89 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Devices/InputDeviceId.h>
#include <AzFramework/Input/User/LocalUserId.h>
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/string/string.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! EBus interface used to send text entry requests to connected input devices
class InputTextEntryRequests : public AZ::EBusTraits
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Options to specify the appearance and/or behavior of any on-screen virtual keyboard that
//! may be displayed to the user for entering text. Depending on the specific implementation,
//! not all of these options will be relevant, in which case they will be completely ignored.
struct VirtualKeyboardOptions
{
AZStd::string m_initialText; //!< The virtual keyboard's initial text
AZStd::string m_titleText; //!< The virtual keyboard's title text
float m_normalizedMinY = 0.0f; //!< The virtual keyboard's minimum y position normalized
LocalUserId m_localUserId = LocalUserIdAny; //!< The local user to operate the keyboard
};
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests can be addressed to a specific InputDeviceId using EBus<>::Event,
//! which should be handled by only one device that has connected to the bus using that id.
//! Input requests can also be sent using EBus<>::Broadcast, in which case they'll be sent
//! to all input devices that have connected to the input event bus regardless of their id.
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests should be handled by only one input device connected to each id
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
////////////////////////////////////////////////////////////////////////////////////////////
//! EBus Trait: requests can be addressed to a specific InputDeviceId
using BusIdType = InputDeviceId;
////////////////////////////////////////////////////////////////////////////////////////////
//! Query whether text entry has already been started
//!
//! Called using either:
//! - EBus<>::Broadcast (any input device can respond to the request)
//! - EBus<>::Event(id) (the given device can respond to the request)
//!
//! \return True if text entry has already been started, false otherwise
virtual bool HasTextEntryStarted() const { return false; }
////////////////////////////////////////////////////////////////////////////////////////////
//! Inform input devices that text input is expected to start (pair with StopTextInput)
//!
//! Called using either:
//! - EBus<>::Broadcast (any input device can respond to the request)
//! - EBus<>::Event(id) (the given device can respond to the request)
//!
//! \param[in] options Used to specify the appearance/behavior of any virtual keyboard shown
virtual void TextEntryStart(const VirtualKeyboardOptions& /*options*/) {}
////////////////////////////////////////////////////////////////////////////////////////////
//! Inform input devices that text input is expected to stop (pair with StartTextInput)
//!
//! Called using either:
//! - EBus<>::Broadcast (any input device can respond to the request)
//! - EBus<>::Event(id) (the given device can respond to the request)
virtual void TextEntryStop() {}
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
virtual ~InputTextEntryRequests() = default;
};
using InputTextEntryRequestBus = AZ::EBus<InputTextEntryRequests>;
} // namespace AzFramework
@@ -0,0 +1,358 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Channels/InputChannel.h>
#include <AzFramework/Input/Devices/InputDevice.h>
#include <AzFramework/Input/Buses/Notifications/InputChannelNotificationBus.h>
#include <AzCore/RTTI/BehaviorContext.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannel::Snapshot::Snapshot(const InputChannel& inputChannel)
: m_channelId(inputChannel.GetInputChannelId())
, m_deviceId(inputChannel.GetInputDevice().GetInputDeviceId())
, m_state(inputChannel.GetState())
, m_value(inputChannel.GetValue())
, m_delta(inputChannel.GetDelta())
, m_localUserId(inputChannel.GetInputDevice().GetAssignedLocalUserId())
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannel::Snapshot::Snapshot(const InputChannelId& channelId,
const InputDeviceId& deviceId,
State state)
: m_channelId(channelId)
, m_deviceId(deviceId)
, m_state(state)
, m_value((state == State::Began || state == State::Updated) ? 1.0f : 0.0f)
, m_delta((state == State::Began) ? 1.0f : ((state == State::Ended) ? -1.0f : 0.0f))
, m_localUserId(0)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannel::Snapshot::Snapshot(const InputChannelId& channelId,
const InputDeviceId& deviceId,
State state,
float value,
float delta)
: m_channelId(channelId)
, m_deviceId(deviceId)
, m_state(state)
, m_value(value)
, m_delta(delta)
, m_localUserId(0)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZ::Vector2 InputChannel::PositionData2D::ConvertToScreenSpaceCoordinates(float screenWidth, float screenHeight) const
{
return AZ::Vector2(m_normalizedPosition.GetX() * screenWidth,
m_normalizedPosition.GetY() * screenHeight);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannel::PositionData2D::UpdateNormalizedPositionAndDelta(const AZ::Vector2& newPosition)
{
const AZ::Vector2 oldPosition = m_normalizedPosition;
m_normalizedPosition = newPosition;
m_normalizedPositionDelta = newPosition - oldPosition;
}
////////////////////////////////////////////////////////////////////////////////////////////////
class InputChannelNotificationBusBehaviorHandler
: public InputChannelNotificationBus::Handler
, public AZ::BehaviorEBusHandler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
AZ_EBUS_BEHAVIOR_BINDER(InputChannelNotificationBusBehaviorHandler
, "{88A23B57-E8EB-49B7-817C-2C85CE0C09E8}"
, AZ::SystemAllocator
, OnInputChannelEvent
);
////////////////////////////////////////////////////////////////////////////////////////////
void OnInputChannelEvent(const InputChannel& inputChannel, bool& o_hasBeenConsumed) override
{
if (!o_hasBeenConsumed)
{
Call(FN_OnInputChannelEvent, &inputChannel, false);
}
}
};
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelRequests::BusIdType::Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<BusIdType>("InputChannelRequest_BusId")
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Attribute(AZ::Script::Attributes::Category, "Input")
->Constructor<const char*>()
->Constructor<const char*, AZ::u32>()
->Property("channelId", BehaviorValueProperty(&BusIdType::m_channelId))
->Property("deviceIndex", BehaviorValueProperty(&BusIdType::m_deviceIndex))
;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Reflect input channel notifications and requests for use in lua.
//
// Input notification example:
//
// function ScriptName:OnActivate()
// self.inputChannelNotificationBus = InputChannelNotificationBus.Connect(self);
// end
//
// function ScriptName:OnDeactivate()
// self.inputChannelNotificationBus:Disconnect(self);
// end
//
// function ScriptName:OnInputChannelEvent(inputChannel)
// Debug.Log("OnInputChannelEvent channelName = " .. inputChannel.channelName)
// Debug.Log("OnInputChannelEvent deviceName = " .. inputChannel.deviceName)
// Debug.Log("OnInputChannelEvent state = " .. inputChannel.state)
// Debug.Log("OnInputChannelEvent value = " .. inputChannel.value)
// end
//
//
// Input request example:
//
// function ScriptName:OnTick(deltaTime, timePoint)
// local channelRequestId = InputChannelRequest_BusId(InputDeviceMouse.mouse_button_left)
// local inputChannel = InputChannelRequestBus.Event.GetInputChannel(channelRequestId)
// if (inputChannel:IsActive()) then
// Debug.Log("Left mouse button active")
// end
// end
//
void InputChannel::Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<InputChannel>()
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::RuntimeOwn)
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Attribute(AZ::Script::Attributes::Category, "Input")
->Property("channelName", [](InputChannel* thisPtr) { return thisPtr->GetInputChannelId().GetName(); }, nullptr)
->Property("deviceName", [](InputChannel* thisPtr) { return thisPtr->GetInputDevice().GetInputDeviceId().GetName(); }, nullptr)
->Property("deviceIndex", [](InputChannel* thisPtr) { return thisPtr->GetInputDevice().GetInputDeviceId().GetIndex(); }, nullptr)
->Property("localUserId", [](InputChannel* thisPtr) { return thisPtr->GetInputDevice().GetAssignedLocalUserId(); }, nullptr)
->Property("state", [](InputChannel* thisPtr) { return thisPtr->GetState(); }, nullptr)
->Property("value", [](InputChannel* thisPtr) { return thisPtr->GetValue(); }, nullptr)
->Property("delta", [](InputChannel* thisPtr) { return thisPtr->GetDelta(); }, nullptr)
->Method("IsStateIdle", &InputChannel::IsStateIdle)
->Method("IsStateBegan", &InputChannel::IsStateBegan)
->Method("IsStateUpdated", &InputChannel::IsStateUpdated)
->Method("IsStateEnded", &InputChannel::IsStateEnded)
->Method("IsActive", &InputChannel::IsActive)
->Enum<int(State::Idle)>("State_Idle")
->Enum<int(State::Began)>("State_Began")
->Enum<int(State::Updated)>("State_Updated")
->Enum<int(State::Ended)>("State_Ended")
;
behaviorContext->EBus<InputChannelNotificationBus>("InputChannelNotificationBus")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Attribute(AZ::Script::Attributes::Category, "Input")
->Handler<InputChannelNotificationBusBehaviorHandler>()
;
InputChannelRequests::BusIdType::Reflect(context);
behaviorContext->EBus<InputChannelRequestBus>("InputChannelRequestBus")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Attribute(AZ::Script::Attributes::Category, "Input")
->Event("GetInputChannel", &InputChannelRequestBus::Events::GetInputChannel)
->Event("SimulateRawInput", &InputChannelRequestBus::Events::SimulateRawInput)
->Event("SimulateRawInput2D", &InputChannelRequestBus::Events::SimulateRawInput2D)
->Event("SimulateRawInput3D", &InputChannelRequestBus::Events::SimulateRawInput3D)
->Event("SimulateRawInputWithPosition2D", &InputChannelRequestBus::Events::SimulateRawInputWithPosition2D)
;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannel::InputChannel(const InputChannelId& inputChannelId, const InputDevice& inputDevice)
: m_inputChannelId(inputChannelId)
, m_inputDevice(inputDevice)
, m_state(State::Idle)
{
const InputChannelRequests::BusIdType busId(m_inputChannelId,
m_inputDevice.GetInputDeviceId().GetIndex());
InputChannelRequestBus::Handler::BusConnect(busId);
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannel::~InputChannel()
{
ResetState();
const InputChannelRequests::BusIdType busId(m_inputChannelId,
m_inputDevice.GetInputDeviceId().GetIndex());
InputChannelRequestBus::Handler::BusDisconnect(busId);
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannel* InputChannel::GetInputChannel() const
{
return this;
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId& InputChannel::GetInputChannelId() const
{
return m_inputChannelId;
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputDevice& InputChannel::GetInputDevice() const
{
return m_inputDevice;
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannel::State InputChannel::GetState() const
{
return m_state;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputChannel::IsStateIdle() const
{
return m_state == State::Idle;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputChannel::IsStateBegan() const
{
return m_state == State::Began;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputChannel::IsStateUpdated() const
{
return m_state == State::Updated;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputChannel::IsStateEnded() const
{
return m_state == State::Ended;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputChannel::IsActive() const
{
return IsStateBegan() || IsStateUpdated();
}
////////////////////////////////////////////////////////////////////////////////////////////////
float InputChannel::GetValue() const
{
return 0.0f;
}
////////////////////////////////////////////////////////////////////////////////////////////////
float InputChannel::GetDelta() const
{
return 0.0f;
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannel::CustomData* InputChannel::GetCustomData() const
{
return nullptr;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputChannel::UpdateState(bool isChannelActive)
{
const State previousState = m_state;
switch (m_state)
{
case State::Idle:
{
if (isChannelActive)
{
m_state = State::Began;
}
}
break;
case State::Began:
{
if (isChannelActive)
{
m_state = State::Updated;
}
else
{
m_state = State::Ended;
}
}
break;
case State::Updated:
{
if (!isChannelActive)
{
m_state = State::Ended;
}
}
break;
case State::Ended:
{
if (!isChannelActive)
{
m_state = State::Idle;
}
else
{
m_state = State::Began;
}
}
break;
}
if (m_state != State::Idle)
{
bool hasBeenConsumed = false;
InputChannelNotificationBus::Broadcast(&InputChannelNotifications::OnInputChannelEvent,
*this,
hasBeenConsumed);
}
return m_state != previousState;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannel::ResetState()
{
// Ensure the channel transitions to the 'Ended' state if it happens to currently be active
if (IsActive())
{
UpdateState(false);
}
// Directly return the channel to the 'Idle' state
m_state = State::Idle;
}
} // namespace AzFramework
@@ -0,0 +1,245 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Buses/Requests/InputChannelRequestBus.h>
#include <AzFramework/Input/Devices/InputDeviceId.h>
#include <AzFramework/Input/User/LocalUserId.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/std/typetraits/is_base_of.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
class InputDevice;
////////////////////////////////////////////////////////////////////////////////////////////////
//! Base class for all input channels that represent the current state of a single input source.
//! Derived classes should provide additional functions that allow their parent input devices to
//! update the state and value(s) of the input channel as raw input is received from the system,
//! and they can (optionally) override the virtual GetCustomData function to return custom data.
class InputChannel : public InputChannelRequestBus::Handler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! State of the input channel (not all channels will go through all states)
enum class State
{
Idle, //!< Examples: inactive or idle, not currently emitting input events
Began, //!< Examples: button pressed, trigger engaged, thumb-stick exits deadzone
Updated, //!< Examples: button held, trigger changed, thumb-stick is outside deadzone
Ended //!< Examples: button released, trigger released, thumb-stick enters deadzone
};
////////////////////////////////////////////////////////////////////////////////////////////
//! Snapshot of an input channel that can be constructed, copied, and stored independently.
struct Snapshot
{
////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputChannel The input channel used to initialize the snapshot
Snapshot(const InputChannel& inputChannel);
////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] channelId The channel id used to initialize the snapshot
//! \param[in] deviceId The device id used to initialize the snapshot
//! \param[in] state The state used to initialize the snapshot
Snapshot(const InputChannelId& channelId,
const InputDeviceId& deviceId,
State state);
////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] channelId The channel id used to initialize the snapshot
//! \param[in] deviceId The device id used to initialize the snapshot
//! \param[in] state The state used to initialize the snapshot
//! \param[in] value The value used to initialize the snapshot
//! \param[in] delta The delta used to initialize the snapshot
Snapshot(const InputChannelId& channelId,
const InputDeviceId& deviceId,
State state,
float value,
float delta);
////////////////////////////////////////////////////////////////////////////////////////
// Variables
InputChannelId m_channelId; //!< The channel id of the input channel
InputDeviceId m_deviceId; //!< The device id of the input channel
State m_state; //!< The state of the input channel
float m_value; //!< The value of the input channel
float m_delta; //!< The delta of the input channel
LocalUserId m_localUserId; //!< The local user id assigned to the input device
};
////////////////////////////////////////////////////////////////////////////////////////////
//! Base struct from which to derive all custom input data
struct CustomData
{
AZ_CLASS_ALLOCATOR(CustomData, AZ::SystemAllocator, 0);
AZ_RTTI(CustomData, "{887E38BB-64AF-4F4E-A1AE-C1B02371F9EC}");
virtual ~CustomData() = default;
};
////////////////////////////////////////////////////////////////////////////////////////////
//! Custom data struct for input channels associated with a 2D position
struct PositionData2D : public CustomData
{
AZ_CLASS_ALLOCATOR(PositionData2D, AZ::SystemAllocator, 0);
AZ_RTTI(PositionData2D, "{354437EC-6BFD-41D4-A0F2-7740018D3589}", CustomData);
virtual ~PositionData2D() = default;
////////////////////////////////////////////////////////////////////////////////////////
//! Convenience function to convert the normalized position to screen space coordinates
//! \param[in] screenWidth The width of the screen to use in the conversion
//! \param[in] screenHeight The height of the screen to use in the conversion
//! \return The position in screen space coordinates
AZ::Vector2 ConvertToScreenSpaceCoordinates(float screenWidth, float screenHeight) const;
////////////////////////////////////////////////////////////////////////////////////////
//! Update both m_normalizedPosition and m_normalizedPositionDelta given a new position
//! \param[in] newNormalizedPosition The new normalized position
void UpdateNormalizedPositionAndDelta(const AZ::Vector2& newNormalizedPosition);
////////////////////////////////////////////////////////////////////////////////////////
//! Normalized screen coordinates, where the top-left of the screen is at (0.0, 0.0) and
//! the bottom-right is at (1.0, 1.0)
AZ::Vector2 m_normalizedPosition = AZ::Vector2(0.5f, 0.5f);
////////////////////////////////////////////////////////////////////////////////////////
//! The delta between the current normalized position and the last one
AZ::Vector2 m_normalizedPositionDelta = AZ::Vector2::CreateZero();
};
////////////////////////////////////////////////////////////////////////////////////////////
//! Alias for verbose shared_ptr class
using SharedPositionData2D = AZStd::shared_ptr<InputChannel::PositionData2D>;
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputChannel, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputChannel, "{1C88625D-D297-4A1C-AE07-E17F88D138F3}");
////////////////////////////////////////////////////////////////////////////////////////////
// Reflection
static void Reflect(AZ::ReflectContext* context);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputChannelId Id of the input channel
//! \param[in] inputDevice Input device that owns the input channel
InputChannel(const InputChannelId& inputChannelId,
const InputDevice& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputChannel);
////////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
~InputChannel() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelRequests::GetInputChannel
const InputChannel* GetInputChannel() const final;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the input channel's id
//! \return Id of the input channel
const InputChannelId& GetInputChannelId() const;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the input channel's device
//! \return Input device that owns the input channel
const InputDevice& GetInputDevice() const;
////////////////////////////////////////////////////////////////////////////////////////////
//! Direct access to the input channel's current state
//! \return The current state of the input channel
State GetState() const;
////////////////////////////////////////////////////////////////////////////////////////////
///@{
//! Indirect access to the input channel's current state
//! \return True if the input channel is currently in the specified state, false otherwise
bool IsStateIdle() const;
bool IsStateBegan() const;
bool IsStateUpdated() const;
bool IsStateEnded() const;
///@}
////////////////////////////////////////////////////////////////////////////////////////////
//! Indirect access to the input channel's current state
//! \return True if the channel is in the 'Began' or 'Updated' states, false otherwise
bool IsActive() const;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the one dimensional float value of the input channel
//! \return The current one dimensional float value of the input channel
virtual float GetValue() const;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the one dimensional float delta of the input channel
//! \return The current one dimensional float delta of the input channel
virtual float GetDelta() const;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to any custom data provided by the input channel
//! \return Pointer to the custom data if it exists, nullptr otherwise
virtual const CustomData* GetCustomData() const;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to any custom data of a specific type provided by the input channel
//! \tparam CustomDataType The specific type of custom data to be returned if it exists
//! \return Pointer to the data if it exists and is of type CustomDataType, nullptr othewise
template<class CustomDataType> const CustomDataType* GetCustomData() const;
////////////////////////////////////////////////////////////////////////////////////////////
//! Update the channel's state based on whether it is active/engaged or inactive/idle, which
//! will broadcast an input event if the channel is left in a non-idle state. Should only be
//! called a maximum of once per channel per frame from InputDeviceRequests::TickInputDevice
//! to ensure input channels broadcast no more than one event each frame (at the same time).
//! \param[in] isChannelActive Whether the input channel is currently active/engaged
//! \return Whether the update resulted in a state transition (was m_state changed)
bool UpdateState(bool isChannelActive);
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelRequests::ResetState
void ResetState() override;
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
const InputChannelId m_inputChannelId; //!< Id of the input channel
const InputDevice& m_inputDevice; //!< Input device that owns the input channel
State m_state; //!< Current state of the input channel
};
////////////////////////////////////////////////////////////////////////////////////////////////
// Inline Implementation
template<class CustomDataType>
inline const CustomDataType* InputChannel::GetCustomData() const
{
static_assert((AZStd::is_base_of<CustomData, CustomDataType>::value),
"Custom input data must inherit from InputChannel::CustomData");
const CustomData* customData = GetCustomData();
return customData ? azdynamic_cast<const CustomDataType*>(customData) : nullptr;
}
} // namespace AzFramework
@@ -0,0 +1,63 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Channels/InputChannelAnalog.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelAnalog::InputChannelAnalog(const InputChannelId& inputChannelId,
const InputDevice& inputDevice)
: InputChannel(inputChannelId, inputDevice)
, m_value(0.0f)
, m_delta(0.0f)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
float InputChannelAnalog::GetValue() const
{
return m_value;
}
////////////////////////////////////////////////////////////////////////////////////////////////
float InputChannelAnalog::GetDelta() const
{
return m_delta;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelAnalog::ResetState()
{
m_value = 0.0f;
m_delta = 0.0f;
InputChannel::ResetState();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelAnalog::SimulateRawInput(float rawValue)
{
ProcessRawInputEvent(rawValue);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelAnalog::ProcessRawInputEvent(float rawValue)
{
const float oldValue = m_value;
m_value = rawValue;
m_delta = rawValue - oldValue;
const bool isChannelActive = (rawValue != 0.0f);
UpdateState(isChannelActive);
}
} // namespace AzFramework
@@ -0,0 +1,82 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Channels/InputChannel.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Class for input channels that emit one dimensional analog input values.
//! Example: game-pad trigger
class InputChannelAnalog : public InputChannel
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputChannelAnalog, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputChannelAnalog, "{C3E76C92-0D00-45F1-AF03-EFF3F1910A0D}", InputChannel);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputChannelId Id of the input channel being constructed
//! \param[in] inputDevice Input device that owns the input channel
explicit InputChannelAnalog(const InputChannelId& inputChannelId,
const InputDevice& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputChannelAnalog);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
~InputChannelAnalog() override = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the one dimensional analog value of the input channel
//! \return The current analogue value of the input channel
float GetValue() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the one dimensional analog delta of the input channel
//! \return Difference between the current and last reported analog values
float GetDelta() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelRequests::ResetState
void ResetState() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelRequests::SimulateRawInput
void SimulateRawInput(float rawValue) override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Process a raw input event, that will in turn update the channel's state based on whether
//! it's active/engaged or inactive/idle, broadcasting an input event if the channel is left
//! in a non-idle state. This function (or InputChannel::UpdateState) should only be called
//! a max of once per channel per frame from InputDeviceRequests::TickInputDevice to ensure
//! that input channels broadcast no more than one event each frame (and at the same time).
//! \param[in] rawValue The raw analog value to process
void ProcessRawInputEvent(float rawValue);
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
float m_value; //!< Current analog value of the input channel
float m_delta; //!< Difference between the current and last reported analog values
};
} // namespace AzFramework
@@ -0,0 +1,57 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Channels/InputChannelAnalogWithPosition2D.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelAnalogWithPosition2D::InputChannelAnalogWithPosition2D(
const InputChannelId& inputChannelId,
const InputDevice& inputDevice)
: InputChannelAnalog(inputChannelId, inputDevice)
, m_positionData()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannel::CustomData* InputChannelAnalogWithPosition2D::GetCustomData() const
{
return &m_positionData;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelAnalogWithPosition2D::ResetState()
{
m_positionData = PositionData2D();
InputChannelAnalog::ResetState();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelAnalogWithPosition2D::SimulateRawInputWithPosition2D(float rawValue,
float normalizedX,
float normalizedY)
{
const RawInputEvent rawValues(normalizedX, normalizedY, rawValue);
ProcessRawInputEvent(rawValues);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelAnalogWithPosition2D::ProcessRawInputEvent(const RawInputEvent& rawValues)
{
const AZ::Vector2 newPosition = AZ::Vector2(rawValues.m_normalizedX, rawValues.m_normalizedY);
m_positionData.UpdateNormalizedPositionAndDelta(newPosition);
InputChannelAnalog::ProcessRawInputEvent(rawValues.m_analogValue);
}
} // namespace AzFramework
@@ -0,0 +1,108 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Channels/InputChannelAnalog.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Class for input channels that emit one dimensional analog input values and a position.
//! Example: touch with pressure and position
class InputChannelAnalogWithPosition2D : public InputChannelAnalog
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Raw analog with position 2D input event
struct RawInputEvent
{
////////////////////////////////////////////////////////////////////////////////////
//! Constructor
explicit RawInputEvent(float normalizedX, float normalizedY, float analogValue)
: m_normalizedX(normalizedX)
, m_normalizedY(normalizedY)
, m_analogValue(analogValue)
{}
////////////////////////////////////////////////////////////////////////////////////
// Default copying
AZ_DEFAULT_COPY(RawInputEvent);
////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
virtual ~RawInputEvent() = default;
////////////////////////////////////////////////////////////////////////////////////
// Variables
float m_normalizedX; //!< The normalized x position of the raw input event
float m_normalizedY; //!< The normalized y position of the raw input event
float m_analogValue; //!< The analog value of the raw input event (0: idle)
};
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputChannelAnalogWithPosition2D, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputChannelAnalogWithPosition2D, "{2C07314B-3294-41F8-9F14-FD5FE4048283}", InputChannel);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputChannelId Id of the input channel being constructed
//! \param[in] inputDevice Input device that owns the input channel
explicit InputChannelAnalogWithPosition2D(const InputChannelId& inputChannelId,
const InputDevice& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputChannelAnalogWithPosition2D);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
~InputChannelAnalogWithPosition2D() override = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the position data associated with the input channel
//! \return Pointer to the position data
const InputChannel::CustomData* GetCustomData() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelRequests::ResetState
void ResetState() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelRequests::SimulateRawInputWithPosition2D
void SimulateRawInputWithPosition2D(float rawValue,
float normalizedX,
float normalizedY) override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Process a raw input event, that will in turn update the channel's state based on whether
//! it's active/engaged or inactive/idle, broadcasting an input event if the channel is left
//! in a non-idle state. This function (or InputChannel::UpdateState) should only be called
//! a max of once per channel per frame from InputDeviceRequests::TickInputDevice to ensure
//! that input channels broadcast no more than one event each frame (and at the same time).
//!
//! Note that this function hides InputChannelAnalog::ProcessRawInputEvent which is intended.
//!
//! \param[in] rawValues The raw analog value and position 2D to process
void ProcessRawInputEvent(const RawInputEvent& rawValues);
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
InputChannel::PositionData2D m_positionData; //!< Current position data
};
} // namespace AzFramework
@@ -0,0 +1,24 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Channels/InputChannelAxis1D.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelAxis1D::InputChannelAxis1D(const InputChannelId& inputChannelId,
const InputDevice& inputDevice)
: InputChannelAnalog(inputChannelId, inputDevice)
{
}
} // namespace AzFramework
@@ -0,0 +1,49 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Channels/InputChannelAnalog.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Class for input channels that emit one dimensional axis input values.
//! Example: game-pad thumb-stick x or y
class InputChannelAxis1D : public InputChannelAnalog
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputChannelAxis1D, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputChannelAxis1D, "{D869AE1C-0409-4811-A9AD-27CD11C7075A}", InputChannel);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputChannelId Id of the input channel being constructed
//! \param[in] inputDevice Input device that owns the input channel
explicit InputChannelAxis1D(const InputChannelId& inputChannelId,
const InputDevice& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputChannelAxis1D);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
~InputChannelAxis1D() override = default;
};
} // namespace AzFramework
@@ -0,0 +1,79 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Channels/InputChannelAxis2D.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelAxis2D::InputChannelAxis2D(const InputChannelId& inputChannelId,
const InputDevice& inputDevice)
: InputChannel(inputChannelId, inputDevice)
, m_axisData2D()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
float InputChannelAxis2D::GetValue() const
{
return m_axisData2D.m_values.GetLength();
}
////////////////////////////////////////////////////////////////////////////////////////////////
float InputChannelAxis2D::GetDelta() const
{
return m_axisData2D.m_deltas.GetLength();
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannel::CustomData* InputChannelAxis2D::GetCustomData() const
{
return &m_axisData2D;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelAxis2D::ResetState()
{
m_axisData2D = AxisData2D();
InputChannel::ResetState();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelAxis2D::SimulateRawInput2D(float rawValueX, float rawValueY)
{
const AZ::Vector2 rawValues(rawValueX, rawValueY);
ProcessRawInputEvent(rawValues);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelAxis2D::ProcessRawInputEvent(const AZ::Vector2& rawValues,
const AZ::Vector2* rawValuesPreDeadZone) // = nullptr
{
const AZ::Vector2 oldValues = m_axisData2D.m_values;
m_axisData2D.m_values = rawValues;
m_axisData2D.m_deltas = rawValues - oldValues;
m_axisData2D.m_preDeadZoneValues = rawValuesPreDeadZone ? *rawValuesPreDeadZone : rawValues;
// The modification of this check to use m_preDeadZoneValues instead of m_values will
// possibly result in events being sent out even while the thumbstick is still idling,
// (depending on the physical hardware and the platform). Although this change to the
// existing behavior could potentially impact other systems, it is simply unavoidable
// if we want any system to be able to access the pre dead-zone thumbstick values, so
// we'll just have to be aware and ensure that any other systems using these specific
// InputChannelAxis2D input events (of which there are few) are checking the m_values
// instead of doing something based simply off receiving the event in the first place.
const bool isChannelActive = (m_axisData2D.m_preDeadZoneValues.GetX() != 0.0f) ||
(m_axisData2D.m_preDeadZoneValues.GetY() != 0.0f);
UpdateState(isChannelActive);
}
} // namespace AzFramework
@@ -0,0 +1,104 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Channels/InputChannel.h>
#include <AzCore/Math/Vector2.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Class for input channels that emit two dimensional axis input values.
//! Example: game-pad thumb-stick x and y
class InputChannelAxis2D : public InputChannel
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Custom data struct for two dimensional axis data
struct AxisData2D : public InputChannel::CustomData
{
AZ_CLASS_ALLOCATOR(AxisData2D, AZ::SystemAllocator, 0);
AZ_RTTI(AxisData2D, "{AA0FF4D4-ED98-4AEE-A3AB-B442287E2B7B}", CustomData);
~AxisData2D() override = default;
AZ::Vector2 m_values = AZ::Vector2::CreateZero();
AZ::Vector2 m_deltas = AZ::Vector2::CreateZero();
AZ::Vector2 m_preDeadZoneValues = AZ::Vector2::CreateZero();
};
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputChannelAxis2D, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputChannelAxis2D, "{03432ABA-C019-401A-B652-C56272FA4667}", InputChannel);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputChannelId Id of the input channel being constructed
//! \param[in] inputDevice Input device that owns the input channel
explicit InputChannelAxis2D(const InputChannelId& inputChannelId,
const InputDevice& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputChannelAxis2D);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
~InputChannelAxis2D() override = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the distance from the origin (length of the vector formed by the axis values)
//! \return The distance from the origin (length of the vector formed by the axis values)
float GetValue() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the total distance moved since last frame
//! \return The total distance moved since last frame
float GetDelta() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the two dimensional axis data associated with the input channel
//! \return Pointer to the two dimensional axis data
const InputChannel::CustomData* GetCustomData() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelRequests::ResetState
void ResetState() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelRequests::SimulateRawInput2D
void SimulateRawInput2D(float rawValueX, float rawValueY) override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Process a raw input event, that will in turn update the channel's state based on whether
//! it's active/engaged or inactive/idle, broadcasting an input event if the channel is left
//! in a non-idle state. This function (or InputChannel::UpdateState) should only be called
//! a max of once per channel per frame from InputDeviceRequests::TickInputDevice to ensure
//! that input channels broadcast no more than one event each frame (and at the same time).
//! If rawValuesPreDeadZone is null, we'll assume it is the same as rawValuesPostDeadZone.
//! \param[in] rawValuesPostDeadZone Raw values after applying a platform-specific deadzone
//! \param[in] rawValuesPreDeadZone Raw values before applying a platform-specific deadzone
void ProcessRawInputEvent(const AZ::Vector2& rawValuesPostDeadZone,
const AZ::Vector2* rawValuesPreDeadZone = nullptr);
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
AxisData2D m_axisData2D; //!< Current two dimensional axis values of the input channel
};
} // namespace AzFramework
@@ -0,0 +1,70 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Channels/InputChannelAxis3D.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelAxis3D::InputChannelAxis3D(const InputChannelId& inputChannelId,
const InputDevice& inputDevice)
: InputChannel(inputChannelId, inputDevice)
, m_axisData3D()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
float InputChannelAxis3D::GetValue() const
{
return m_axisData3D.m_values.GetLength();
}
////////////////////////////////////////////////////////////////////////////////////////////////
float InputChannelAxis3D::GetDelta() const
{
return m_axisData3D.m_deltas.GetLength();
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannel::CustomData* InputChannelAxis3D::GetCustomData() const
{
return &m_axisData3D;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelAxis3D::ResetState()
{
m_axisData3D = AxisData3D();
InputChannel::ResetState();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelAxis3D::SimulateRawInput3D(float rawValueX, float rawValueY, float rawValueZ)
{
const AZ::Vector3 rawValues(rawValueX, rawValueY, rawValueZ);
ProcessRawInputEvent(rawValues);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelAxis3D::ProcessRawInputEvent(const AZ::Vector3& rawValues)
{
const AZ::Vector3 oldValues = m_axisData3D.m_values;
m_axisData3D.m_values = rawValues;
m_axisData3D.m_deltas = rawValues - oldValues;
const bool isChannelActive = (m_axisData3D.m_values.GetX() != 0.0f) ||
(m_axisData3D.m_values.GetY() != 0.0f) ||
(m_axisData3D.m_values.GetZ() != 0.0f);
UpdateState(isChannelActive);
}
} // namespace AzFramework
@@ -0,0 +1,100 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Channels/InputChannel.h>
#include <AzCore/Math/Vector3.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Class for input channels that emit three dimensional axis input values.
//! Example: motion sensor data (acceleration, rotation, or magnetic field)
class InputChannelAxis3D : public InputChannel
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Custom data struct for three dimensional axis data
struct AxisData3D : public InputChannel::CustomData
{
AZ_CLASS_ALLOCATOR(AxisData3D, AZ::SystemAllocator, 0);
AZ_RTTI(AxisData3D, "{ABD4447B-34C6-4D17-B4E8-5B62209C14EA}", CustomData);
~AxisData3D() override = default;
AZ::Vector3 m_values = AZ::Vector3::CreateZero();
AZ::Vector3 m_deltas = AZ::Vector3::CreateZero();
};
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputChannelAxis3D, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputChannelAxis3D, "{40CE7BF6-12C3-4B88-AEB7-B3D63E686650}", InputChannel);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputChannelId Id of the input channel being constructed
//! \param[in] inputDevice Input device that owns the input channel
explicit InputChannelAxis3D(const InputChannelId& inputChannelId,
const InputDevice& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputChannelAxis3D);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
~InputChannelAxis3D() override = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the distance from the origin (length of the vector formed by the axis values)
//! \return The distance from the origin (length of the vector formed by the axis values)
float GetValue() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the total distance moved since last frame
//! \return The total distance moved since last frame
float GetDelta() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the three dimensional axis data associated with the input channel
//! \return Pointer to the three dimensional axis data
const InputChannel::CustomData* GetCustomData() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelRequests::ResetState
void ResetState() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelRequests::SimulateRawInput3D
void SimulateRawInput3D(float rawValueX, float rawValueY, float rawValueZ) override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Process a raw input event, that will in turn update the channel's state based on whether
//! it's active/engaged or inactive/idle, broadcasting an input event if the channel is left
//! in a non-idle state. This function (or InputChannel::UpdateState) should only be called
//! a max of once per channel per frame from InputDeviceRequests::TickInputDevice to ensure
//! that input channels broadcast no more than one event each frame (and at the same time).
//! \param[in] rawValues The raw axis values to process
void ProcessRawInputEvent(const AZ::Vector3& rawValues);
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
AxisData3D m_axisData3D; //!< Current three dimensional axis values of the input channel
};
} // namespace AzFramework
@@ -0,0 +1,59 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Channels/InputChannelDelta.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelDelta::InputChannelDelta(const InputChannelId& inputChannelId,
const InputDevice& inputDevice)
: InputChannel(inputChannelId, inputDevice)
, m_delta(0.0f)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
float InputChannelDelta::GetValue() const
{
return m_delta;
}
////////////////////////////////////////////////////////////////////////////////////////////////
float InputChannelDelta::GetDelta() const
{
return m_delta;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelDelta::ResetState()
{
m_delta = 0.0f;
InputChannel::ResetState();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelDelta::SimulateRawInput(float rawValue)
{
ProcessRawInputEvent(rawValue);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelDelta::ProcessRawInputEvent(float rawValue)
{
m_delta = rawValue;
const bool isChannelActive = (m_delta != 0.0f);
UpdateState(isChannelActive);
}
} // namespace AzFramework
@@ -0,0 +1,81 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Channels/InputChannel.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Class for input channels that emit one dimensional delta input values, where the 'delta' and
//! 'value' is one and the same, unlike other input channels that calculate the delta themselves.
class InputChannelDelta : public InputChannel
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputChannelDelta, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputChannelDelta, "{3AE8E55C-08E2-4258-B42E-3C3B6304B5D2}", InputChannel);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputChannelId Id of the input channel being constructed
//! \param[in] inputDevice Input device that owns the input channel
explicit InputChannelDelta(const InputChannelId& inputChannelId,
const InputDevice& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputChannelDelta);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
~InputChannelDelta() override = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the one dimensional delta/value of the input channel, same as GetDelta
//! \return The current delta/value of the input channel
float GetValue() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the one dimensional delta/value of the input channel, same as GetValue
//! \return The current delta/value of the input channel
float GetDelta() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelRequests::ResetState
void ResetState() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelRequests::SimulateRawInput
void SimulateRawInput(float rawValue) override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Process a raw input event, that will in turn update the channel's state based on whether
//! it's active/engaged or inactive/idle, broadcasting an input event if the channel is left
//! in a non-idle state. This function (or InputChannel::UpdateState) should only be called
//! a max of once per channel per frame from InputDeviceRequests::TickInputDevice to ensure
//! that input channels broadcast no more than one event each frame (and at the same time).
//! \param[in] rawValue The raw delta value to process
void ProcessRawInputEvent(float rawValue);
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
float m_delta; //!< The current delta/value of the input channel
};
} // namespace AzFramework
@@ -0,0 +1,33 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Channels/InputChannelDeltaWithSharedPosition2D.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelDeltaWithSharedPosition2D::InputChannelDeltaWithSharedPosition2D(
const AzFramework::InputChannelId& inputChannelId,
const InputDevice& inputDevice,
const SharedPositionData2D& sharedPositionData)
: InputChannelDelta(inputChannelId, inputDevice)
, m_sharedPositionData(sharedPositionData)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannel::CustomData* InputChannelDeltaWithSharedPosition2D::GetCustomData() const
{
return m_sharedPositionData.get();
}
} // namespace LmbrCentral
@@ -0,0 +1,64 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Channels/InputChannelDelta.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Class for input channels that emit one dimensional delta input values and share a position.
//! Examples: mouse movement
class InputChannelDeltaWithSharedPosition2D : public InputChannelDelta
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputChannelDeltaWithSharedPosition2D, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputChannelDeltaWithSharedPosition2D, "{F7EC8D6F-DC27-4CDF-80F4-EFA7DCC33837}", InputChannel);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputChannelId Id of the input channel being constructed
//! \param[in] inputDevice Input device that owns the input channel
//! \param[in] sharedPositionData Shared ptr to the common position data
explicit InputChannelDeltaWithSharedPosition2D(
const AzFramework::InputChannelId& inputChannelId,
const InputDevice& inputDevice,
const SharedPositionData2D& sharedPositionData);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputChannelDeltaWithSharedPosition2D);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
~InputChannelDeltaWithSharedPosition2D() override = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the shared position data
//! \return Pointer to the shared position data
const AzFramework::InputChannel::CustomData* GetCustomData() const override;
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
const SharedPositionData2D m_sharedPositionData; //!< Shared position data
};
} // namespace LmbrCentral
@@ -0,0 +1,55 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Channels/InputChannelDigital.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelDigital::InputChannelDigital(const InputChannelId& inputChannelId,
const InputDevice& inputDevice)
: InputChannel(inputChannelId, inputDevice)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
float InputChannelDigital::GetValue() const
{
return IsActive() ? 1.0f : 0.0f;
}
////////////////////////////////////////////////////////////////////////////////////////////////
float InputChannelDigital::GetDelta() const
{
switch (GetState())
{
case State::Idle: return 0.0f;
case State::Began: return 1.0f;
case State::Updated: return 0.0f;
case State::Ended: return -1.0f;
}
return 0.0f;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelDigital::SimulateRawInput(float rawValue)
{
ProcessRawInputEvent(rawValue != 0.0f ? true : false);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelDigital::ProcessRawInputEvent(bool rawValue)
{
UpdateState(rawValue);
}
} // namespace AzFramework
@@ -0,0 +1,72 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Channels/InputChannel.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Class representing input channels that emit one dimensional digital input values.
//! Examples: game-pad button, keyboard key
class InputChannelDigital : public InputChannel
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputChannelDigital, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputChannelDigital, "{07BD463B-0E1C-47B5-849D-3C09F9D1B468}", InputChannel);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputChannelId Id of the input channel being constructed
//! \param[in] inputDevice Input device that owns the input channel
explicit InputChannelDigital(const InputChannelId& inputChannelId,
const InputDevice& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputChannelDigital);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
~InputChannelDigital() override = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the digital value of the input channel (1.0f or 0.0f)
//! \return The current digital value of the input channel
float GetValue() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the digital delta of the input channel (1.0f, -1.0f, or 0.0f)
//! \return Difference between the current and last reported digital values
float GetDelta() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelRequests::SimulateRawInput
void SimulateRawInput(float rawValue) override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Process a raw input event, that will in turn update the channel's state based on whether
//! it's active/engaged or inactive/idle, broadcasting an input event if the channel is left
//! in a non-idle state. This function (or InputChannel::UpdateState) should only be called
//! a max of once per channel per frame from InputDeviceRequests::TickInputDevice to ensure
//! that input channels broadcast no more than one event each frame (and at the same time).
//! \param[in] rawValue The raw digital value to process
void ProcessRawInputEvent(bool rawValue);
};
} // namespace AzFramework
@@ -0,0 +1,57 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Channels/InputChannelDigitalWithPosition2D.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelDigitalWithPosition2D::InputChannelDigitalWithPosition2D(
const InputChannelId& inputChannelId,
const InputDevice& inputDevice)
: InputChannelDigital(inputChannelId, inputDevice)
, m_positionData()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannel::CustomData* InputChannelDigitalWithPosition2D::GetCustomData() const
{
return &m_positionData;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelDigitalWithPosition2D::ResetState()
{
m_positionData = PositionData2D();
InputChannelDigital::ResetState();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelDigitalWithPosition2D::SimulateRawInputWithPosition2D(float rawValue,
float normalizedX,
float normalizedY)
{
const RawInputEvent rawValues(normalizedX, normalizedY, rawValue != 0.0f ? true : false);
ProcessRawInputEvent(rawValues);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelDigitalWithPosition2D::ProcessRawInputEvent(const RawInputEvent& rawValues)
{
const AZ::Vector2 newPosition = AZ::Vector2(rawValues.m_normalizedX, rawValues.m_normalizedY);
m_positionData.UpdateNormalizedPositionAndDelta(newPosition);
InputChannelDigital::ProcessRawInputEvent(rawValues.m_digitalValue);
}
} // namespace AzFramework
@@ -0,0 +1,108 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Channels/InputChannelDigital.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Class for input channels that emit one dimensional digital input values and a position.
//! Examples: touch
class InputChannelDigitalWithPosition2D : public InputChannelDigital
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Raw digital with position 2D input event
struct RawInputEvent
{
////////////////////////////////////////////////////////////////////////////////////
//! Constructor
explicit RawInputEvent(float normalizedX, float normalizedY, bool digitalValue)
: m_normalizedX(normalizedX)
, m_normalizedY(normalizedY)
, m_digitalValue(digitalValue)
{}
////////////////////////////////////////////////////////////////////////////////////
// Default copying
AZ_DEFAULT_COPY(RawInputEvent);
////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
~RawInputEvent() = default;
////////////////////////////////////////////////////////////////////////////////////
// Variables
float m_normalizedX; //!< The normalized x position of the raw input event
float m_normalizedY; //!< The normalized y position of the raw input event
bool m_digitalValue; //!< The digital value of the raw input event (on|off)
};
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputChannelDigitalWithPosition2D, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputChannelDigitalWithPosition2D, "{5D3EC355-D359-47B7-9984-5B19D68FEC06}", InputChannel);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputChannelId Id of the input channel being constructed
//! \param[in] inputDevice Input device that owns the input channel
explicit InputChannelDigitalWithPosition2D(const InputChannelId& inputChannelId,
const InputDevice& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputChannelDigitalWithPosition2D);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
virtual ~InputChannelDigitalWithPosition2D() override = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the position data associated with the input channel
//! \return Pointer to the position data
const InputChannel::CustomData* GetCustomData() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelRequests::ResetState
void ResetState() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelRequests::SimulateRawInputWithPosition2D
void SimulateRawInputWithPosition2D(float rawValue,
float normalizedX,
float normalizedY) override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Process a raw input event, that will in turn update the channel's state based on whether
//! it's active/engaged or inactive/idle, broadcasting an input event if the channel is left
//! in a non-idle state. This function (or InputChannel::UpdateState) should only be called
//! a max of once per channel per frame from InputDeviceRequests::TickInputDevice to ensure
//! that input channels broadcast no more than one event each frame (and at the same time).
//!
//! Note that this function hides InputChannelDigital::ProcessRawInputEvent which is intended.
//!
//! \param[in] rawValues The raw digital value and position 2D to process
void ProcessRawInputEvent(const RawInputEvent& rawValues);
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
InputChannel::PositionData2D m_positionData; //!< Current position data
};
} // namespace AzFramework
@@ -0,0 +1,70 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Channels/InputChannelDigitalWithSharedModifierKeyStates.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
bool ModifierKeyStates::IsActive(ModifierKeyMask modifierKey) const
{
return (static_cast<int>(m_activeModifierKeys) & static_cast<int>(modifierKey)) != 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void ModifierKeyStates::SetActive(ModifierKeyMask modifierKey, bool active)
{
const int newState = active ?
static_cast<int>(m_activeModifierKeys) | static_cast<int>(modifierKey) :
static_cast<int>(m_activeModifierKeys) & ~(static_cast<int>(modifierKey));
m_activeModifierKeys = static_cast<ModifierKeyMask>(newState);
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelDigitalWithSharedModifierKeyStates::InputChannelDigitalWithSharedModifierKeyStates(
const InputChannelId& inputChannelId,
const InputDevice& inputDevice,
SharedModifierKeyStates& sharedModifierKeyStates,
ModifierKeyMask correspondingModifierKey) // = ModifierKeyMask::None
: InputChannelDigital(inputChannelId, inputDevice)
, m_sharedModifierKeyStates(sharedModifierKeyStates)
, m_correspondingModifierKey(correspondingModifierKey)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannel::CustomData* InputChannelDigitalWithSharedModifierKeyStates::GetCustomData() const
{
return m_sharedModifierKeyStates.get();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelDigitalWithSharedModifierKeyStates::ResetState()
{
if (m_correspondingModifierKey != ModifierKeyMask::None)
{
m_sharedModifierKeyStates->SetActive(m_correspondingModifierKey, false);
}
InputChannelDigital::ResetState();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelDigitalWithSharedModifierKeyStates::ProcessRawInputEvent(bool rawValue)
{
if (m_correspondingModifierKey != ModifierKeyMask::None)
{
m_sharedModifierKeyStates->SetActive(m_correspondingModifierKey, rawValue);
}
InputChannelDigital::ProcessRawInputEvent(rawValue);
}
} // namespace AzFramework
@@ -0,0 +1,136 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Channels/InputChannelDigital.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
enum class ModifierKeyMask : int
{
None = 0x0000,
AltL = 0x0001,
AltR = 0x0002,
CtrlL = 0x0004,
CtrlR = 0x0008,
ShiftL = 0x0010,
ShiftR = 0x0020,
SuperL = 0x0040,
SuperR = 0x0080,
AltAny = (AltL | AltR),
CtrlAny = (CtrlL | CtrlR),
ShiftAny = (ShiftL | ShiftR),
SuperAny = (SuperL | SuperR)
};
////////////////////////////////////////////////////////////////////////////////////////////////
//! Custom data struct to store the current state of all modifier keys
struct ModifierKeyStates : public InputChannel::CustomData
{
AZ_CLASS_ALLOCATOR(ModifierKeyStates, AZ::SystemAllocator, 0);
AZ_RTTI(ModifierKeyStates, "{999937EC-6BFD-41F4-A0F2-7990018D3589}", CustomData);
virtual ~ModifierKeyStates() = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Get the bitmask of active modifier keys
//! \return The bitmask of active modifier keys
ModifierKeyMask GetActiveModifierKeys() const { return m_activeModifierKeys; }
////////////////////////////////////////////////////////////////////////////////////////////
//! Query whether the specified modifier key is active
//! \param[in] modifierKey The modifier key to check
//! \return True if the modifier key is active, false otherwise
bool IsActive(ModifierKeyMask modifierKey) const;
private:
////////////////////////////////////////////////////////////////////////////////////////////
//! Set the active state of the specified modifier key
//! \param[in] modifierKey The modifier key to set
//! \param[in] active The active state to set
void SetActive(ModifierKeyMask modifierKey, bool active);
friend class InputChannelDigitalWithSharedModifierKeyStates;
ModifierKeyMask m_activeModifierKeys = ModifierKeyMask::None; //!< Active modifier keys
};
////////////////////////////////////////////////////////////////////////////////////////////////
//! Alias for verbose shared_ptr class
using SharedModifierKeyStates = AZStd::shared_ptr<ModifierKeyStates>;
////////////////////////////////////////////////////////////////////////////////////////////////
//! Class for input channels that emit digital input values and a shared modifier key state.
//! Examples: keyboard key
class InputChannelDigitalWithSharedModifierKeyStates : public InputChannelDigital
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputChannelDigitalWithSharedModifierKeyStates, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputChannelDigitalWithSharedModifierKeyStates, "{DAA5C9F4-B833-4F3D-AED5-B8B87BB8FF72}", InputChannel);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputChannelId Id of the input channel being constructed
//! \param[in] inputDevice Input device that owns the input channel
//! \param[in] sharedModifierKeyStates The shared modifier key states
//! \param[in] correspondingModifierKey The corresponding modifier key
explicit InputChannelDigitalWithSharedModifierKeyStates(
const InputChannelId& inputChannelId,
const InputDevice& inputDevice,
SharedModifierKeyStates& sharedModifierKeyStates,
ModifierKeyMask correspondingModifierKey = ModifierKeyMask::None);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputChannelDigitalWithSharedModifierKeyStates);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
virtual ~InputChannelDigitalWithSharedModifierKeyStates() override = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the shared modifier key state data associated with the input channel
//! \return Pointer to the modifier key state data
const InputChannel::CustomData* GetCustomData() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelRequests::ResetState
void ResetState() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Process a raw input event, that will in turn update the channel's state based on whether
//! it's active/engaged or inactive/idle, broadcasting an input event if the channel is left
//! in a non-idle state. This function (or InputChannel::UpdateState) should only be called
//! a max of once per channel per frame from InputDeviceRequests::TickInputDevice to ensure
//! that input channels broadcast no more than one event each frame (and at the same time).
//!
//! Note that this function hides InputChannelDigital::ProcessRawInputEvent which is intended.
//!
//! \param[in] rawValues The raw digital value to process
void ProcessRawInputEvent(bool rawValue);
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
SharedModifierKeyStates m_sharedModifierKeyStates; //!< The shared modifier key states
ModifierKeyMask m_correspondingModifierKey; //!< The corresponding modifier key
};
} // namespace AzFramework
@@ -0,0 +1,33 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Channels/InputChannelDigitalWithSharedPosition2D.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelDigitalWithSharedPosition2D::InputChannelDigitalWithSharedPosition2D(
const AzFramework::InputChannelId& inputChannelId,
const InputDevice& inputDevice,
const SharedPositionData2D& sharedPositionData)
: InputChannelDigital(inputChannelId, inputDevice)
, m_sharedPositionData(sharedPositionData)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannel::CustomData* InputChannelDigitalWithSharedPosition2D::GetCustomData() const
{
return m_sharedPositionData.get();
}
} // namespace LmbrCentral
@@ -0,0 +1,64 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Channels/InputChannelDigital.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Class for input channels that emit one dimensional digital input value and share a position.
//! Examples: mouse button
class InputChannelDigitalWithSharedPosition2D : public InputChannelDigital
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputChannelDigitalWithSharedPosition2D, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputChannelDigitalWithSharedPosition2D, "{EFCEC2F4-A81F-4218-A878-1D7676FB1FC6}", InputChannel);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputChannelId Id of the input channel being constructed
//! \param[in] inputDevice Input device that owns the input channel
//! \param[in] sharedPositionData Shared ptr to the common position data
explicit InputChannelDigitalWithSharedPosition2D(
const AzFramework::InputChannelId& inputChannelId,
const InputDevice& inputDevice,
const SharedPositionData2D& sharedPositionData);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputChannelDigitalWithSharedPosition2D);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
~InputChannelDigitalWithSharedPosition2D() override = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the shared position data
//! \return Pointer to the shared position data
const AzFramework::InputChannel::CustomData* GetCustomData() const override;
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
const SharedPositionData2D m_sharedPositionData; //!< Shared position data
};
} // namespace LmbrCentral
@@ -0,0 +1,82 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Channels/InputChannelId.h>
#include <AzCore/RTTI/BehaviorContext.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelId::Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<InputChannelId>()
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Attribute(AZ::Script::Attributes::Category, "Input")
->Constructor<const char*>()
->Property("name", [](InputChannelId* thisPtr) { return thisPtr->GetName(); }, nullptr)
;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelId::InputChannelId(const char* name)
: m_crc32(name)
{
memset(m_name, 0, AZ_ARRAY_SIZE(m_name));
azstrncpy(m_name, NAME_BUFFER_SIZE, name, MAX_NAME_LENGTH);
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelId::InputChannelId(const InputChannelId& other)
: m_crc32(other.m_crc32)
{
memset(m_name, 0, AZ_ARRAY_SIZE(m_name));
azstrcpy(m_name, NAME_BUFFER_SIZE, other.m_name);
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelId& InputChannelId::operator=(const InputChannelId& other)
{
azstrcpy(m_name, NAME_BUFFER_SIZE, other.m_name);
m_crc32 = other.m_crc32;
return *this;
}
////////////////////////////////////////////////////////////////////////////////////////////////
const char* InputChannelId::GetName() const
{
return m_name;
}
////////////////////////////////////////////////////////////////////////////////////////////////
const AZ::Crc32& InputChannelId::GetNameCrc32() const
{
return m_crc32;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputChannelId::operator==(const InputChannelId& other) const
{
return (m_crc32 == other.m_crc32);
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputChannelId::operator!=(const InputChannelId& other) const
{
return !(*this == other);
}
} // namespace AzFramework
@@ -0,0 +1,101 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Math/Crc.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/std/hash.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Class that identifies a specific input channel
class InputChannelId
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
// Constants
static const int NAME_BUFFER_SIZE = 64;
static const int MAX_NAME_LENGTH = NAME_BUFFER_SIZE - 1;
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputChannelId, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_TYPE_INFO(InputChannelId, "{7004B466-F6B4-41EF-AFFD-96A456121271}");
////////////////////////////////////////////////////////////////////////////////////////////
// Reflection
static void Reflect(AZ::ReflectContext* context);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] name Name of the input channel (will be truncated if exceeds MAX_NAME_LENGTH)
explicit InputChannelId(const char* name = "");
////////////////////////////////////////////////////////////////////////////////////////////
//! Copy constructor
//! \param[in] other Another instance of the class to copy from
InputChannelId(const InputChannelId& other);
////////////////////////////////////////////////////////////////////////////////////////////
//! Copy assignment operator
//! \param[in] other Another instance of the class to copy from
InputChannelId& operator=(const InputChannelId& other);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
~InputChannelId() = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the input channel's name
//! \return Name of the input channel
const char* GetName() const;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the crc32 of the input channel's name
//! \return crc32 of the input channel name
const AZ::Crc32& GetNameCrc32() const;
////////////////////////////////////////////////////////////////////////////////////////////
///@{
//! Equality comparison operator
//! \param[in] other Another instance of the class to compare for equality
bool operator==(const InputChannelId& other) const;
bool operator!=(const InputChannelId& other) const;
///@}
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
char m_name[NAME_BUFFER_SIZE]; //!< Name of the input channel
AZ::Crc32 m_crc32; //!< Crc32 of the input channel
};
} // namespace AzFramework
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AZStd
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Hash structure specialization for InputChannelId
template<> struct hash<AzFramework::InputChannelId>
{
inline size_t operator()(const AzFramework::InputChannelId& inputChannelId) const
{
return inputChannelId.GetNameCrc32();
}
};
} // namespace AZStd
@@ -0,0 +1,49 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Channels/InputChannelQuaternion.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelQuaternion::InputChannelQuaternion(const InputChannelId& inputChannelId,
const InputDevice& inputDevice)
: InputChannel(inputChannelId, inputDevice)
, m_quaternionData()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannel::CustomData* InputChannelQuaternion::GetCustomData() const
{
return &m_quaternionData;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelQuaternion::ResetState()
{
m_quaternionData = QuaternionData();
InputChannel::ResetState();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelQuaternion::ProcessRawInputEvent(const AZ::Quaternion& rawValue)
{
const AZ::Quaternion oldValue = m_quaternionData.m_value;
m_quaternionData.m_value = rawValue;
m_quaternionData.m_delta = oldValue.GetInverseFast() * rawValue;
const bool isChannelActive = !rawValue.IsIdentity();
UpdateState(isChannelActive);
}
} // namespace AzFramework
@@ -0,0 +1,96 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Channels/InputChannel.h>
#include <AzCore/Math/Quaternion.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Class for input channels that emit quaternion input values.
//! Example: motion sensor data (orientation)
class InputChannelQuaternion : public InputChannel
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Custom data struct for three dimensional axis data
struct QuaternionData : public InputChannel::CustomData
{
AZ_CLASS_ALLOCATOR(QuaternionData, AZ::SystemAllocator, 0);
AZ_RTTI(QuaternionData, "{D1B11964-0ABB-4539-ACB2-7156B6CDEB90}", CustomData);
~QuaternionData() override = default;
AZ::Quaternion m_value = AZ::Quaternion::CreateIdentity();
AZ::Quaternion m_delta = AZ::Quaternion::CreateIdentity();
};
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputChannelQuaternion, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputChannelQuaternion, "{91E34916-F7C8-46AD-A239-DB9DC24C237A}", InputChannel);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputChannelId Id of the input channel being constructed
//! \param[in] inputDevice Input device that owns the input channel
InputChannelQuaternion(const InputChannelId& inputChannelId,
const InputDevice& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputChannelQuaternion);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
~InputChannelQuaternion() override = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Quaternion input channels do not represent a single value
//! \return 0.0f
float GetValue() const override { return 0.0f; }
////////////////////////////////////////////////////////////////////////////////////////////
//! Quaternion input channels do not represent a single value
//! \return 0.0f
float GetDelta() const override { return 0.0f; }
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the quaternion data associated with the input channel
//! \return Pointer to the quaternion data
const InputChannel::CustomData* GetCustomData() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelRequests::ResetState
void ResetState() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Process a raw input event, that will in turn update the channel's state based on whether
//! it's active/engaged or inactive/idle, broadcasting an input event if the channel is left
//! in a non-idle state. This function (or InputChannel::UpdateState) should only be called
//! a max of once per channel per frame from InputDeviceRequests::TickInputDevice to ensure
//! that input channels broadcast no more than one event each frame (and at the same time).
//! \param[in] rawValues The raw quaternion value to process
void ProcessRawInputEvent(const AZ::Quaternion& rawValue);
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
QuaternionData m_quaternionData; //!< Current quaternion value of the input channel
};
} // namespace AzFramework
@@ -0,0 +1,157 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Contexts/InputContext.h>
#include <AzFramework/Input/Mappings/InputMapping.h>
#include <AzCore/Debug/Trace.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
InputContext::InputContext(const char* name)
: InputDevice(InputDeviceId(name))
, InputChannelEventListener()
, m_inputChannelsById()
, m_inputMappingsById()
, m_consumesProcessedInput(false)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputContext::InputContext(const char* name, const InitData& initData)
: InputDevice(InputDeviceId(name))
, InputChannelEventListener(initData.filter, initData.priority, initData.autoActivate)
, m_inputChannelsById()
, m_inputMappingsById()
, m_consumesProcessedInput(initData.consumesProcessedInput)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputContext::~InputContext()
{
// We have to reset the state of all the input mappings owned by this input context.
for (auto& inputMappingById : m_inputMappingsById)
{
inputMappingById.second->ResetState();
}
Deactivate();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputContext::Activate()
{
InputChannelEventListener::Connect();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputContext::Deactivate()
{
InputChannelEventListener::Disconnect();
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputContext::AddInputMapping(AZStd::shared_ptr<InputMapping> inputMapping)
{
if (!inputMapping)
{
AZ_Warning("InputContext", false, "Cannot add a null input mapping");
return false;
}
const InputChannelId& inputMappingId = inputMapping->GetInputChannelId();
if (&inputMapping->GetInputDevice() != this)
{
AZ_Warning("InputContext", false,
"Input context (%s) is not the parent of input mapping with id: %s, cannot add",
GetInputDeviceId().GetName(), inputMappingId.GetName());
return false;
}
const auto it = m_inputMappingsById.find(inputMappingId);
if (it != m_inputMappingsById.end())
{
AZ_Warning("InputContext", false,
"Input context (%s) already contains an input mapping with id: %s, cannot add",
GetInputDeviceId().GetName(), inputMappingId.GetName());
return false;
}
m_inputMappingsById[inputMappingId] = AZStd::move(inputMapping);
m_inputChannelsById[inputMappingId] = inputMapping.get();
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputContext::RemoveInputMapping(const InputChannelId& inputMappingId)
{
const auto mappingIt = m_inputMappingsById.find(inputMappingId);
if (mappingIt == m_inputMappingsById.end())
{
AZ_Warning("InputContext", false,
"Input context (%s) does not contain an input mapping with id: %s, cannot remove",
GetInputDeviceId().GetName(), inputMappingId.GetName());
return false;
}
const auto channelIt = m_inputChannelsById.find(inputMappingId);
AZ_Assert(channelIt != m_inputChannelsById.end(),
"InputContext (%s) contains an InputMapping (%s) that was found in m_inputMappingsById but not m_inputChannelsById",
GetInputDeviceId().GetName(), inputMappingId.GetName());
m_inputChannelsById.erase(channelIt);
m_inputMappingsById.erase(mappingIt);
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputDevice::InputChannelByIdMap& InputContext::GetInputChannelsById() const
{
return m_inputChannelsById;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputContext::IsSupported() const
{
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputContext::IsConnected() const
{
return InputChannelEventListener::BusIsConnected();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputContext::TickInputDevice()
{
for (auto& inputMappingById : m_inputMappingsById)
{
inputMappingById.second->OnTick();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputContext::OnInputChannelEventFiltered(const InputChannel& inputChannel)
{
for (auto& inputMappingById : m_inputMappingsById)
{
if (inputMappingById.second->ProcessPotentialSourceInputEvent(inputChannel))
{
return m_consumesProcessedInput;
}
}
return false;
}
} // namespace AzFramework
@@ -0,0 +1,140 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Devices/InputDevice.h>
#include <AzFramework/Input/Events/InputChannelEventListener.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
class InputMapping;
////////////////////////////////////////////////////////////////////////////////////////////////
//! An InputContext is an InputEventListener that owns a collection of InputMapping objects and
//! forwards input events to them. By inheriting from InputEventListener the InputContext gains
//! access to the same priority and 'consumed' systems that all other input event listeners use,
//! meaning they can be interleaved with any other engine / gameplay system that consumes input.
//! InputContext also inherits from InputDevice, which while unintuitive is necessary for input
//! mapping instances that need to be created by passing a reference to the parent input device.
class InputContext : public InputDevice
, public InputChannelEventListener
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Custom data struct used to initialize input contexts
struct InitData
{
////////////////////////////////////////////////////////////////////////////////////////
//! The filter used to determine whether an input event should be handled
AZStd::shared_ptr<InputChannelEventFilter> filter;
////////////////////////////////////////////////////////////////////////////////////////
//! The priority used to sort relative to other input event listeners
AZ::s32 priority = InputChannelEventListener::GetPriorityDefault();
////////////////////////////////////////////////////////////////////////////////////////
//! Whether to activate (connect to the input notification bus) on construction
bool autoActivate = false;
////////////////////////////////////////////////////////////////////////////////////////
//! Should the input context consume input that is processed by any of its input mappings?
bool consumesProcessedInput = false;
};
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputContext, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] name Unique, will be truncated if exceeds InputDeviceId::MAX_NAME_LENGTH = 64
explicit InputContext(const char* name);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] name Unique, will be truncated if exceeds InputDeviceId::MAX_NAME_LENGTH = 64
//! \param[in] initData Custom data struct used to initialize the input context
InputContext(const char* name, const InitData& initData);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputContext);
////////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
~InputContext() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Activate the input context (this just calls the base InputChannelEventListener::Connect)
void Activate();
////////////////////////////////////////////////////////////////////////////////////////////
//! Deactivate the input context (this just calls the base InputChannelEventListener::Disconnect)
void Deactivate();
////////////////////////////////////////////////////////////////////////////////////////////
//! Add an input mapping to this input context (the input context will share ownership of it)
//! \param[in] inputMapping Shared pointer to the input mapping to add to this input context
//! \return True if the input mapping was added to this input context, false otherwise
bool AddInputMapping(AZStd::shared_ptr<InputMapping> inputMapping);
////////////////////////////////////////////////////////////////////////////////////////////
//! Remove an input mapping from this input context (the shared ownership will be released)
//! \param[in] inputMappingId The id of the input mapping to remove from this input context
//! \return True if the input mapping was removed from this input context, false otherwise
bool RemoveInputMapping(const InputChannelId& inputMappingId);
protected:
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::GetInputChannelsById
const InputChannelByIdMap& GetInputChannelsById() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::IsSupported
bool IsSupported() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::IsConnected
bool IsConnected() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDeviceRequests::TickInputDevice
void TickInputDevice() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelEventListener::OnInputChannelEventFiltered
bool OnInputChannelEventFiltered(const InputChannel& inputChannel) override;
private:
////////////////////////////////////////////////////////////////////////////////////////////
//! Alias for verbose container class
using InputMappingsByIdMap = AZStd::unordered_map<InputChannelId, AZStd::shared_ptr<InputMapping>>;
////////////////////////////////////////////////////////////////////////////////////////////
///@{
//! All input channels/mappings owned by this input context. We need to store two separate
//! containers so that we can return the correct type from GetInputChannelsById while also
//! maintaining shared ownership of input mappings added via the AddInputMapping function.
InputChannelByIdMap m_inputChannelsById;
InputMappingsByIdMap m_inputMappingsById;
///@}
////////////////////////////////////////////////////////////////////////////////////////////
//! Should this input context consume input that is processed by any of its input mappings?
bool m_consumesProcessedInput = false;
};
} // namespace AzFramework
@@ -0,0 +1,559 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h>
#include <AzFramework/Input/Utils/AdjustAnalogInputForDeadZone.h>
#include <AzCore/RTTI/BehaviorContext.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
const char* InputDeviceGamepad::Name("gamepad");
const InputDeviceId InputDeviceGamepad::IdForIndex0(Name, 0);
const InputDeviceId InputDeviceGamepad::IdForIndex1(Name, 1);
const InputDeviceId InputDeviceGamepad::IdForIndex2(Name, 2);
const InputDeviceId InputDeviceGamepad::IdForIndex3(Name, 3);
const InputDeviceId InputDeviceGamepad::IdForIndexN(AZ::u32 n) { return InputDeviceId(Name, n); }
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceGamepad::IsGamepadDevice(const InputDeviceId& inputDeviceId)
{
return (inputDeviceId.GetNameCrc32() == IdForIndex0.GetNameCrc32());
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceGamepad::Button::A("gamepad_button_a");
const InputChannelId InputDeviceGamepad::Button::B("gamepad_button_b");
const InputChannelId InputDeviceGamepad::Button::X("gamepad_button_x");
const InputChannelId InputDeviceGamepad::Button::Y("gamepad_button_y");
const InputChannelId InputDeviceGamepad::Button::L1("gamepad_button_l1");
const InputChannelId InputDeviceGamepad::Button::R1("gamepad_button_r1");
const InputChannelId InputDeviceGamepad::Button::L3("gamepad_button_l3");
const InputChannelId InputDeviceGamepad::Button::R3("gamepad_button_r3");
const InputChannelId InputDeviceGamepad::Button::DU("gamepad_button_d_up");
const InputChannelId InputDeviceGamepad::Button::DD("gamepad_button_d_down");
const InputChannelId InputDeviceGamepad::Button::DL("gamepad_button_d_left");
const InputChannelId InputDeviceGamepad::Button::DR("gamepad_button_d_right");
const InputChannelId InputDeviceGamepad::Button::Start("gamepad_button_start");
const InputChannelId InputDeviceGamepad::Button::Select("gamepad_button_select");
const AZStd::array<InputChannelId, 14> InputDeviceGamepad::Button::All =
{{
A,
B,
X,
Y,
L1,
R1,
L3,
R3,
DU,
DD,
DL,
DR,
Start,
Select
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceGamepad::Trigger::L2("gamepad_trigger_l2");
const InputChannelId InputDeviceGamepad::Trigger::R2("gamepad_trigger_r2");
const AZStd::array<InputChannelId, 2> InputDeviceGamepad::Trigger::All =
{{
L2,
R2
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceGamepad::ThumbStickAxis2D::L("gamepad_thumbstick_l");
const InputChannelId InputDeviceGamepad::ThumbStickAxis2D::R("gamepad_thumbstick_r");
const AZStd::array<InputChannelId, 2> InputDeviceGamepad::ThumbStickAxis2D::All =
{{
L,
R
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceGamepad::ThumbStickAxis1D::LX("gamepad_thumbstick_l_x");
const InputChannelId InputDeviceGamepad::ThumbStickAxis1D::LY("gamepad_thumbstick_l_y");
const InputChannelId InputDeviceGamepad::ThumbStickAxis1D::RX("gamepad_thumbstick_r_x");
const InputChannelId InputDeviceGamepad::ThumbStickAxis1D::RY("gamepad_thumbstick_r_y");
const AZStd::array<InputChannelId, 4> InputDeviceGamepad::ThumbStickAxis1D::All =
{{
LX,
LY,
RX,
RY
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceGamepad::ThumbStickDirection::LU("gamepad_thumbstick_l_up");
const InputChannelId InputDeviceGamepad::ThumbStickDirection::LD("gamepad_thumbstick_l_down");
const InputChannelId InputDeviceGamepad::ThumbStickDirection::LL("gamepad_thumbstick_l_left");
const InputChannelId InputDeviceGamepad::ThumbStickDirection::LR("gamepad_thumbstick_l_right");
const InputChannelId InputDeviceGamepad::ThumbStickDirection::RU("gamepad_thumbstick_r_up");
const InputChannelId InputDeviceGamepad::ThumbStickDirection::RD("gamepad_thumbstick_r_down");
const InputChannelId InputDeviceGamepad::ThumbStickDirection::RL("gamepad_thumbstick_r_left");
const InputChannelId InputDeviceGamepad::ThumbStickDirection::RR("gamepad_thumbstick_r_right");
const AZStd::array<InputChannelId, 8> InputDeviceGamepad::ThumbStickDirection::All =
{{
LU,
LD,
LL,
LR,
RU,
RD,
RL,
RR
}};
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceGamepad::Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
// Unfortunately it doesn't seem possible to reflect anything through BehaviorContext
// using lambdas which capture variables from the enclosing scope. So we are manually
// reflecting all input channel names, instead of just iterating over them like this:
//
// auto classBuilder = behaviorContext->Class<InputDeviceGamepad>();
// for (const InputChannelId& channelId : Button::All)
// {
// const char* channelName = channelId.GetName();
// classBuilder->Constant(channelName, [channelName]() { return channelName; });
// }
behaviorContext->Class<InputDeviceGamepad>()
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::RuntimeOwn)
->Constant("name", BehaviorConstant(IdForIndex0.GetName()))
->Constant(Button::A.GetName(), BehaviorConstant(Button::A.GetName()))
->Constant(Button::B.GetName(), BehaviorConstant(Button::B.GetName()))
->Constant(Button::X.GetName(), BehaviorConstant(Button::X.GetName()))
->Constant(Button::Y.GetName(), BehaviorConstant(Button::Y.GetName()))
->Constant(Button::L1.GetName(), BehaviorConstant(Button::L1.GetName()))
->Constant(Button::R1.GetName(), BehaviorConstant(Button::R1.GetName()))
->Constant(Button::L3.GetName(), BehaviorConstant(Button::L3.GetName()))
->Constant(Button::R3.GetName(), BehaviorConstant(Button::R3.GetName()))
->Constant(Button::DU.GetName(), BehaviorConstant(Button::DU.GetName()))
->Constant(Button::DD.GetName(), BehaviorConstant(Button::DD.GetName()))
->Constant(Button::DL.GetName(), BehaviorConstant(Button::DL.GetName()))
->Constant(Button::DR.GetName(), BehaviorConstant(Button::DR.GetName()))
->Constant(Button::Start.GetName(), BehaviorConstant(Button::Start.GetName()))
->Constant(Button::Select.GetName(), BehaviorConstant(Button::Select.GetName()))
->Constant(Trigger::L2.GetName(), BehaviorConstant(Trigger::L2.GetName()))
->Constant(Trigger::R2.GetName(), BehaviorConstant(Trigger::R2.GetName()))
->Constant(ThumbStickAxis2D::L.GetName(), BehaviorConstant(ThumbStickAxis2D::L.GetName()))
->Constant(ThumbStickAxis2D::R.GetName(), BehaviorConstant(ThumbStickAxis2D::R.GetName()))
->Constant(ThumbStickAxis1D::LX.GetName(), BehaviorConstant(ThumbStickAxis1D::LX.GetName()))
->Constant(ThumbStickAxis1D::LY.GetName(), BehaviorConstant(ThumbStickAxis1D::LY.GetName()))
->Constant(ThumbStickAxis1D::RX.GetName(), BehaviorConstant(ThumbStickAxis1D::RX.GetName()))
->Constant(ThumbStickAxis1D::RY.GetName(), BehaviorConstant(ThumbStickAxis1D::RY.GetName()))
->Constant(ThumbStickDirection::LU.GetName(), BehaviorConstant(ThumbStickDirection::LU.GetName()))
->Constant(ThumbStickDirection::LD.GetName(), BehaviorConstant(ThumbStickDirection::LD.GetName()))
->Constant(ThumbStickDirection::LL.GetName(), BehaviorConstant(ThumbStickDirection::LL.GetName()))
->Constant(ThumbStickDirection::LR.GetName(), BehaviorConstant(ThumbStickDirection::LR.GetName()))
->Constant(ThumbStickDirection::RU.GetName(), BehaviorConstant(ThumbStickDirection::RU.GetName()))
->Constant(ThumbStickDirection::RD.GetName(), BehaviorConstant(ThumbStickDirection::RD.GetName()))
->Constant(ThumbStickDirection::RL.GetName(), BehaviorConstant(ThumbStickDirection::RL.GetName()))
->Constant(ThumbStickDirection::RR.GetName(), BehaviorConstant(ThumbStickDirection::RR.GetName()))
;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceGamepad::InputDeviceGamepad()
: InputDeviceGamepad(0) // Delegated constructor
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceGamepad::InputDeviceGamepad(AZ::u32 index)
: InputDevice(InputDeviceId(Name, index))
, m_allChannelsById()
, m_buttonChannelsById()
, m_triggerChannelsById()
, m_thumbStickAxis1DChannelsById()
, m_thumbStickAxis2DChannelsById()
, m_thumbStickDirectionChannelsById()
, m_pimpl()
, m_implementationRequestHandler(*this)
{
// Create all digital button input channels
for (const InputChannelId& channelId : Button::All)
{
InputChannelDigital* channel = aznew InputChannelDigital(channelId, *this);
m_allChannelsById[channelId] = channel;
m_buttonChannelsById[channelId] = channel;
}
// Create all analog trigger input channels
for (const InputChannelId& channelId : Trigger::All)
{
InputChannelAnalog* channel = aznew InputChannelAnalog(channelId, *this);
m_allChannelsById[channelId] = channel;
m_triggerChannelsById[channelId] = channel;
}
// Create all thumb-stick 1D axis input channels
for (const InputChannelId& channelId : ThumbStickAxis1D::All)
{
InputChannelAxis1D* channel = aznew InputChannelAxis1D(channelId, *this);
m_allChannelsById[channelId] = channel;
m_thumbStickAxis1DChannelsById[channelId] = channel;
}
// Create all thumb-stick 2D axis input channels
for (const InputChannelId& channelId : ThumbStickAxis2D::All)
{
InputChannelAxis2D* channel = aznew InputChannelAxis2D(channelId, *this);
m_allChannelsById[channelId] = channel;
m_thumbStickAxis2DChannelsById[channelId] = channel;
}
// Create all thumb-stick direction input channels
for (const InputChannelId& channelId : ThumbStickDirection::All)
{
InputChannelAnalog* channel = aznew InputChannelAnalog(channelId, *this);
m_allChannelsById[channelId] = channel;
m_thumbStickDirectionChannelsById[channelId] = channel;
}
// Create the platform specific implementation
m_pimpl.reset(Implementation::Create(*this));
// Connect to the haptic feedback request bus
InputHapticFeedbackRequestBus::Handler::BusConnect(GetInputDeviceId());
// Connect to the light bar request bus
InputLightBarRequestBus::Handler::BusConnect(GetInputDeviceId());
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceGamepad::~InputDeviceGamepad()
{
// Disconnect from the light bar request bus
InputLightBarRequestBus::Handler::BusDisconnect(GetInputDeviceId());
// Disconnect from the haptic feedback request bus
InputHapticFeedbackRequestBus::Handler::BusDisconnect(GetInputDeviceId());
// Destroy the platform specific implementation
m_pimpl.reset();
// Destroy all thumb-stick direction input channels
for (const auto& channelById : m_thumbStickDirectionChannelsById)
{
delete channelById.second;
}
// Destroy all thumb-stick 2D axis input channels
for (const auto& channelById : m_thumbStickAxis2DChannelsById)
{
delete channelById.second;
}
// Destroy all thumb-stick 1D axis input channels
for (const auto& channelById : m_thumbStickAxis1DChannelsById)
{
delete channelById.second;
}
// Destroy all analog trigger input channels
for (const auto& channelById : m_triggerChannelsById)
{
delete channelById.second;
}
// Destroy all digital button input channels
for (const auto& channelById : m_buttonChannelsById)
{
delete channelById.second;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
LocalUserId InputDeviceGamepad::GetAssignedLocalUserId() const
{
return m_pimpl ? m_pimpl->GetAssignedLocalUserId() : InputDevice::GetAssignedLocalUserId();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceGamepad::PromptLocalUserSignIn() const
{
if (m_pimpl)
{
m_pimpl->PromptLocalUserSignIn();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputDevice::InputChannelByIdMap& InputDeviceGamepad::GetInputChannelsById() const
{
return m_allChannelsById;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceGamepad::IsSupported() const
{
return m_pimpl != nullptr;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceGamepad::IsConnected() const
{
return m_pimpl ? m_pimpl->IsConnected() : false;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceGamepad::GetPhysicalKeyOrButtonText(const InputChannelId& inputChannelId,
AZStd::string& o_keyOrButtonText) const
{
// First see if the button has platform specific text
if (m_pimpl && m_pimpl->GetPhysicalKeyOrButtonText(inputChannelId, o_keyOrButtonText))
{
return;
}
if (inputChannelId == Button::A) { o_keyOrButtonText = "A"; }
else if (inputChannelId == Button::B) { o_keyOrButtonText = "B"; }
else if (inputChannelId == Button::X) { o_keyOrButtonText = "X"; }
else if (inputChannelId == Button::Y) { o_keyOrButtonText = "Y"; }
else if (inputChannelId == Button::L1) { o_keyOrButtonText = "L1"; }
else if (inputChannelId == Button::R1) { o_keyOrButtonText = "R1"; }
else if (inputChannelId == Button::L3) { o_keyOrButtonText = "L3"; }
else if (inputChannelId == Button::R3) { o_keyOrButtonText = "R3"; }
else if (inputChannelId == Button::DU) { o_keyOrButtonText = "D-pad Up"; }
else if (inputChannelId == Button::DD) { o_keyOrButtonText = "D-pad Down"; }
else if (inputChannelId == Button::DL) { o_keyOrButtonText = "D-pad Left"; }
else if (inputChannelId == Button::DR) { o_keyOrButtonText = "D-pad Right"; }
else if (inputChannelId == Button::Start) { o_keyOrButtonText = "Start"; }
else if (inputChannelId == Button::Select) { o_keyOrButtonText = "Select"; }
else if (inputChannelId == Trigger::L2) { o_keyOrButtonText = "L2"; }
else if (inputChannelId == Trigger::R2) { o_keyOrButtonText = "R2"; }
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceGamepad::TickInputDevice()
{
if (m_pimpl)
{
m_pimpl->TickInputDevice();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceGamepad::SetVibration(float leftMotorSpeedNormalized,
float rightMotorSpeedNormalized)
{
if (m_pimpl)
{
m_pimpl->SetVibration(leftMotorSpeedNormalized, rightMotorSpeedNormalized);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceGamepad::SetLightBarColor(const AZ::Color& color)
{
if (m_pimpl)
{
m_pimpl->SetLightBarColor(color);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceGamepad::ResetLightBarColor()
{
if (m_pimpl)
{
m_pimpl->ResetLightBarColor();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceGamepad::Implementation::Implementation(InputDeviceGamepad& inputDevice)
: m_inputDevice(inputDevice)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceGamepad::Implementation::~Implementation()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
LocalUserId InputDeviceGamepad::Implementation::GetAssignedLocalUserId() const
{
return GetInputDeviceIndex();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceGamepad::Implementation::BroadcastInputDeviceConnectedEvent() const
{
m_inputDevice.BroadcastInputDeviceConnectedEvent();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceGamepad::Implementation::BroadcastInputDeviceDisconnectedEvent() const
{
m_inputDevice.BroadcastInputDeviceDisconnectedEvent();
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceGamepad::Implementation::RawGamepadState::RawGamepadState(
const DigitalButtonIdByBitMaskMap& digitalButtonIdsByBitMask)
: m_digitalButtonIdsByBitMask(digitalButtonIdsByBitMask)
, m_digitalButtonStates(0)
, m_triggerButtonLState(0.0f)
, m_triggerButtonRState(0.0f)
, m_thumbStickLeftXState(0.0f)
, m_thumbStickLeftYState(0.0f)
, m_thumbStickRightXState(0.0f)
, m_thumbStickRightYState(0.0f)
, m_triggerMaximumValue(1.0f)
, m_triggerDeadZoneValue(0.0f)
, m_thumbStickMaximumValue(1.0f)
, m_thumbStickLeftDeadZone(0.0f)
, m_thumbStickRightDeadZone(0.0f)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceGamepad::Implementation::RawGamepadState::Reset()
{
m_digitalButtonStates = 0;
m_triggerButtonLState = 0.0f;
m_triggerButtonRState = 0.0f;
m_thumbStickLeftXState = 0.0f;
m_thumbStickLeftYState = 0.0f;
m_thumbStickRightXState = 0.0f;
m_thumbStickRightYState = 0.0f;
}
////////////////////////////////////////////////////////////////////////////////////////////////
float InputDeviceGamepad::Implementation::RawGamepadState::GetLeftTriggerAdjustedForDeadZoneAndNormalized() const
{
return AdjustForDeadZoneAndNormalizeAnalogInput(m_triggerButtonLState,
m_triggerDeadZoneValue,
m_triggerMaximumValue);
}
////////////////////////////////////////////////////////////////////////////////////////////////
float InputDeviceGamepad::Implementation::RawGamepadState::GetRightTriggerAdjustedForDeadZoneAndNormalized() const
{
return AdjustForDeadZoneAndNormalizeAnalogInput(m_triggerButtonRState,
m_triggerDeadZoneValue,
m_triggerMaximumValue);
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZ::Vector2 InputDeviceGamepad::Implementation::RawGamepadState::GetLeftThumbStickAdjustedForDeadZoneAndNormalized() const
{
return AdjustForDeadZoneAndNormalizeThumbStickInput(m_thumbStickLeftXState,
m_thumbStickLeftYState,
m_thumbStickLeftDeadZone,
m_thumbStickMaximumValue);
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZ::Vector2 InputDeviceGamepad::Implementation::RawGamepadState::GetRightThumbStickAdjustedForDeadZoneAndNormalized() const
{
return AdjustForDeadZoneAndNormalizeThumbStickInput(m_thumbStickRightXState,
m_thumbStickRightYState,
m_thumbStickRightDeadZone,
m_thumbStickMaximumValue);
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZ::Vector2 InputDeviceGamepad::Implementation::RawGamepadState::GetLeftThumbStickNormalizedValues() const
{
return AZ::Vector2(m_thumbStickLeftXState / m_thumbStickMaximumValue,
m_thumbStickLeftYState / m_thumbStickMaximumValue);
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZ::Vector2 InputDeviceGamepad::Implementation::RawGamepadState::GetRightThumbStickNormalizedValues() const
{
return AZ::Vector2(m_thumbStickRightXState / m_thumbStickMaximumValue,
m_thumbStickRightYState / m_thumbStickMaximumValue);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceGamepad::Implementation::ProcessRawGamepadState(
const RawGamepadState& rawGamepadState)
{
// Update digital button channels
for (const auto& digitalButtonIdByBitMaskPair : rawGamepadState.m_digitalButtonIdsByBitMask)
{
const AZ::u32 buttonState = (rawGamepadState.m_digitalButtonStates & digitalButtonIdByBitMaskPair.first);
const InputChannelId& channelId = *(digitalButtonIdByBitMaskPair.second);
m_inputDevice.m_buttonChannelsById[channelId]->ProcessRawInputEvent(buttonState != 0);
}
// Update the left analog trigger button channel
const float valueL2 = rawGamepadState.GetLeftTriggerAdjustedForDeadZoneAndNormalized();
m_inputDevice.m_triggerChannelsById[InputDeviceGamepad::Trigger::L2]->ProcessRawInputEvent(valueL2);
// Update the right analog trigger button channel
const float valueR2 = rawGamepadState.GetRightTriggerAdjustedForDeadZoneAndNormalized();
m_inputDevice.m_triggerChannelsById[InputDeviceGamepad::Trigger::R2]->ProcessRawInputEvent(valueR2);
// Update the left thumb-stick channels
const AZ::Vector2 valuesLeftThumb = rawGamepadState.GetLeftThumbStickAdjustedForDeadZoneAndNormalized();
const AZ::Vector2 valuesLeftThumbPreDeadZone = rawGamepadState.GetLeftThumbStickNormalizedValues();
m_inputDevice.m_thumbStickAxis2DChannelsById[InputDeviceGamepad::ThumbStickAxis2D::L]->ProcessRawInputEvent(valuesLeftThumb, &valuesLeftThumbPreDeadZone);
m_inputDevice.m_thumbStickAxis1DChannelsById[InputDeviceGamepad::ThumbStickAxis1D::LX]->ProcessRawInputEvent(valuesLeftThumb.GetX());
m_inputDevice.m_thumbStickAxis1DChannelsById[InputDeviceGamepad::ThumbStickAxis1D::LY]->ProcessRawInputEvent(valuesLeftThumb.GetY());
const float leftStickUp = AZ::GetClamp(valuesLeftThumb.GetY(), 0.0f, 1.0f);
const float leftStickDown = fabsf(AZ::GetClamp(valuesLeftThumb.GetY(), -1.0f, 0.0f));
const float leftStickLeft = fabsf(AZ::GetClamp(valuesLeftThumb.GetX(), -1.0f, 0.0f));
const float leftStickRight = AZ::GetClamp(valuesLeftThumb.GetX(), 0.0f, 1.0f);
m_inputDevice.m_thumbStickDirectionChannelsById[InputDeviceGamepad::ThumbStickDirection::LU]->ProcessRawInputEvent(leftStickUp);
m_inputDevice.m_thumbStickDirectionChannelsById[InputDeviceGamepad::ThumbStickDirection::LD]->ProcessRawInputEvent(leftStickDown);
m_inputDevice.m_thumbStickDirectionChannelsById[InputDeviceGamepad::ThumbStickDirection::LL]->ProcessRawInputEvent(leftStickLeft);
m_inputDevice.m_thumbStickDirectionChannelsById[InputDeviceGamepad::ThumbStickDirection::LR]->ProcessRawInputEvent(leftStickRight);
// Update the right thumb-stick channels
const AZ::Vector2 valuesRightThumb = rawGamepadState.GetRightThumbStickAdjustedForDeadZoneAndNormalized();
const AZ::Vector2 valuesRightThumbPreDeadZone = rawGamepadState.GetRightThumbStickNormalizedValues();
m_inputDevice.m_thumbStickAxis2DChannelsById[InputDeviceGamepad::ThumbStickAxis2D::R]->ProcessRawInputEvent(valuesRightThumb, &valuesRightThumbPreDeadZone);
m_inputDevice.m_thumbStickAxis1DChannelsById[InputDeviceGamepad::ThumbStickAxis1D::RX]->ProcessRawInputEvent(valuesRightThumb.GetX());
m_inputDevice.m_thumbStickAxis1DChannelsById[InputDeviceGamepad::ThumbStickAxis1D::RY]->ProcessRawInputEvent(valuesRightThumb.GetY());
const float rightStickUp = AZ::GetClamp(valuesRightThumb.GetY(), 0.0f, 1.0f);
const float rightStickDown = fabsf(AZ::GetClamp(valuesRightThumb.GetY(), -1.0f, 0.0f));
const float rightStickLeft = fabsf(AZ::GetClamp(valuesRightThumb.GetX(), -1.0f, 0.0f));
const float rightStickRight = AZ::GetClamp(valuesRightThumb.GetX(), 0.0f, 1.0f);
m_inputDevice.m_thumbStickDirectionChannelsById[InputDeviceGamepad::ThumbStickDirection::RU]->ProcessRawInputEvent(rightStickUp);
m_inputDevice.m_thumbStickDirectionChannelsById[InputDeviceGamepad::ThumbStickDirection::RD]->ProcessRawInputEvent(rightStickDown);
m_inputDevice.m_thumbStickDirectionChannelsById[InputDeviceGamepad::ThumbStickDirection::RL]->ProcessRawInputEvent(rightStickLeft);
m_inputDevice.m_thumbStickDirectionChannelsById[InputDeviceGamepad::ThumbStickDirection::RR]->ProcessRawInputEvent(rightStickRight);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceGamepad::Implementation::ResetInputChannelStates()
{
m_inputDevice.ResetInputChannelStates();
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZ::u32 InputDeviceGamepad::Implementation::GetInputDeviceIndex() const
{
return m_inputDevice.GetInputDeviceId().GetIndex();
}
} // namespace AzFramework
@@ -0,0 +1,415 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Buses/Requests/InputHapticFeedbackRequestBus.h>
#include <AzFramework/Input/Buses/Requests/InputLightBarRequestBus.h>
#include <AzFramework/Input/Channels/InputChannelAnalog.h>
#include <AzFramework/Input/Channels/InputChannelAxis1D.h>
#include <AzFramework/Input/Channels/InputChannelAxis2D.h>
#include <AzFramework/Input/Channels/InputChannelDigital.h>
#include <AzFramework/Input/Devices/InputDevice.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Defines a generic game-pad input device, including the ids of all associated input channels.
//! Platform specific implementations are defined as private implementations so that creating an
//! instance of this generic class will work correctly on any platform supporting game-pad input,
//! while providing access to the device name and associated channel ids on any platform through
//! the 'null' implementation (primarily so that the editor can use them to setup input mappings).
class InputDeviceGamepad : public InputDevice
, public InputHapticFeedbackRequestBus::Handler
, public InputLightBarRequestBus::Handler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! The name used to identify any game-pad input device
static const char* Name;
////////////////////////////////////////////////////////////////////////////////////////////
//! The id used to identify a game-pad input device with a specific index
///@{
static const InputDeviceId IdForIndex0;
static const InputDeviceId IdForIndex1;
static const InputDeviceId IdForIndex2;
static const InputDeviceId IdForIndex3;
static const InputDeviceId IdForIndexN(AZ::u32 n);
///@}
////////////////////////////////////////////////////////////////////////////////////////////
//! Check whether an input device id identifies a gamepad (regardless of index)
//! \param[in] inputDeviceId The input device id to check
//! \return True if the input device id identifies a gamepad, false otherwise
static bool IsGamepadDevice(const InputDeviceId& inputDeviceId);
////////////////////////////////////////////////////////////////////////////////////////////
//! Get the maximum number of gamepads that are supported on the current platform
//! \return The maximum number of gamepads that are supported on the current platform
static AZ::u32 GetMaxSupportedGamepads();
////////////////////////////////////////////////////////////////////////////////////////////
//! All the input channel ids that identify game-pad digital button input
struct Button
{
static const InputChannelId A; //!< The bottom diamond face button
static const InputChannelId B; //!< The right diamond face button
static const InputChannelId X; //!< The left diamond face button
static const InputChannelId Y; //!< The top diamond face button
static const InputChannelId L1; //!< The top-left shoulder bumper button
static const InputChannelId R1; //!< The top-right shoulder bumper button
static const InputChannelId L3; //!< The left thumb-stick click button
static const InputChannelId R3; //!< The right thumb-stick click button
static const InputChannelId DU; //!< The up directional pad button
static const InputChannelId DD; //!< The down directional pad button
static const InputChannelId DL; //!< The left directional pad button
static const InputChannelId DR; //!< The right directional pad button
static const InputChannelId Start; //!< The start/pause/options button
static const InputChannelId Select; //!< The select/back button
//!< All digital game-pad button ids
static const AZStd::array<InputChannelId, 14> All;
};
////////////////////////////////////////////////////////////////////////////////////////////
//! All the input channel ids that identify game-pad analog trigger input
struct Trigger
{
static const InputChannelId L2; //!< The bottom-left shoulder trigger
static const InputChannelId R2; //!< The bottom-right shoulder trigger
//!< All analog game-pad trigger ids
static const AZStd::array<InputChannelId, 2> All;
};
////////////////////////////////////////////////////////////////////////////////////////////
//! All the input channel ids that identify game-pad thumb-stick 2D axis input
struct ThumbStickAxis2D
{
static const InputChannelId L; //!< The left-hand thumb-stick
static const InputChannelId R; //!< The right-hand thumb-stick
//!< All game-pad thumb-stick 2D axis input channel ids
static const AZStd::array<InputChannelId, 2> All;
};
////////////////////////////////////////////////////////////////////////////////////////////
//! All the input channel ids that identify game-pad thumb-stick 1D axis input
struct ThumbStickAxis1D
{
static const InputChannelId LX; //!< X-axis of the left-hand thumb-stick
static const InputChannelId LY; //!< Y-axis of the left-hand thumb-stick
static const InputChannelId RX; //!< X-axis of the right-hand thumb-stick
static const InputChannelId RY; //!< Y-axis of the right-hand thumb-stick
//!< All game-pad thumb-stick 1D axis input channel ids
static const AZStd::array<InputChannelId, 4> All;
};
////////////////////////////////////////////////////////////////////////////////////////////
//! All the input channel ids that identify game-pad thumb-stick directional input
struct ThumbStickDirection
{
static const InputChannelId LU; //!< Up on the left-hand thumb-stick
static const InputChannelId LD; //!< Down on the left-hand thumb-stick
static const InputChannelId LL; //!< Left on the left-hand thumb-stick
static const InputChannelId LR; //!< Right on the left-hand thumb-stick
static const InputChannelId RU; //!< Up on the left-hand thumb-stick
static const InputChannelId RD; //!< Down on the left-hand thumb-stick
static const InputChannelId RL; //!< Left on the left-hand thumb-stick
static const InputChannelId RR; //!< Right on the left-hand thumb-stick
//!< All game-pad thumb-stick directional input channel ids
static const AZStd::array<InputChannelId, 8> All;
};
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputDeviceGamepad, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputDeviceGamepad, "{16652E28-4B60-4852-BBD0-CB6A2D1B7377}", InputDevice);
////////////////////////////////////////////////////////////////////////////////////////////
// Reflection
static void Reflect(AZ::ReflectContext* context);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
explicit InputDeviceGamepad();
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] index Index of the game-pad device
explicit InputDeviceGamepad(AZ::u32 index);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputDeviceGamepad);
////////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
~InputDeviceGamepad() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::GetAssignedLocalUserId
LocalUserId GetAssignedLocalUserId() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::PromptLocalUserSignIn
void PromptLocalUserSignIn() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::GetInputChannelsById
const InputChannelByIdMap& GetInputChannelsById() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::IsSupported
bool IsSupported() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::IsConnected
bool IsConnected() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDeviceRequests::GetPhysicalKeyOrButtonText
void GetPhysicalKeyOrButtonText(const InputChannelId& inputChannelId,
AZStd::string& o_keyOrButtonText) const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDeviceRequests::TickInputDevice
void TickInputDevice() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputHapticFeedbackRequests::SetVibration
void SetVibration(float leftMotorSpeedNormalized, float rightMotorSpeedNormalized) override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputLightBarRequests::SetLightBarColor
void SetLightBarColor(const AZ::Color& color) override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputLightBarRequests::ResetLightBarColor
void ResetLightBarColor() override;
protected:
////////////////////////////////////////////////////////////////////////////////////////////
///@{
//! Alias for verbose container class
using ButtonChannelByIdMap = AZStd::unordered_map<InputChannelId, InputChannelDigital*>;
using TriggerChannelByIdMap = AZStd::unordered_map<InputChannelId, InputChannelAnalog*>;
using ThumbStickAxis1DChannelByIdMap = AZStd::unordered_map<InputChannelId, InputChannelAxis1D*>;
using ThumbStickAxis2DChannelByIdMap = AZStd::unordered_map<InputChannelId, InputChannelAxis2D*>;
using ThumbStickDirectionChannelByIdMap = AZStd::unordered_map<InputChannelId, InputChannelAnalog*>;
///@}
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
InputChannelByIdMap m_allChannelsById; //!< All game-pad input channels by id
ButtonChannelByIdMap m_buttonChannelsById; //!< All digital button channels by id
TriggerChannelByIdMap m_triggerChannelsById; //!< All analog trigger channels by id
ThumbStickAxis1DChannelByIdMap m_thumbStickAxis1DChannelsById; //!< All thumb-stick axis 1D channels by id
ThumbStickAxis2DChannelByIdMap m_thumbStickAxis2DChannelsById; //!< All thumb-stick axis 2D channels by id
ThumbStickDirectionChannelByIdMap m_thumbStickDirectionChannelsById; //!< All thumb-stick direction channels by id
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Base class for platform specific implementations of game-pad input devices
class Implementation
{
public:
////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(Implementation, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////
//! Default factory create function
//! \param[in] inputDevice Reference to the input device being implemented
static Implementation* Create(InputDeviceGamepad& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputDevice Reference to the input device being implemented
Implementation(InputDeviceGamepad& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(Implementation);
////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
virtual ~Implementation();
////////////////////////////////////////////////////////////////////////////////////////
//! Access to the input device's currently assigned local user id
//! \return Id of the local user currently assigned to the input device
virtual LocalUserId GetAssignedLocalUserId() const;
////////////////////////////////////////////////////////////////////////////////////////
//! Prompt a local user sign-in request from this input device
virtual void PromptLocalUserSignIn() const {}
////////////////////////////////////////////////////////////////////////////////////////
//! Query the connected state of the input device
//! \return True if the input device is currently connected, false otherwise
virtual bool IsConnected() const = 0;
////////////////////////////////////////////////////////////////////////////////////////
//! Set the current vibration (force-feedback) speed of the gamepads motors
//! \param[in] leftMotorSpeedNormalized Speed of the left (large/low frequency) motor
//! \param[in] rightMotorSpeedNormalized Speed of the right (small/high frequency) motor
virtual void SetVibration(float leftMotorSpeedNormalized,
float rightMotorSpeedNormalized) = 0;
////////////////////////////////////////////////////////////////////////////////////////
//! Set the current light bar color of the gamepad (if one exists)
//! \param[in] color The color to set the gamepad's light bar
virtual void SetLightBarColor(const AZ::Color& color) { AZ_UNUSED(color); }
////////////////////////////////////////////////////////////////////////////////////////
//! Reset the light bar color of the gamepad (if one exists) to it's default
virtual void ResetLightBarColor() {}
////////////////////////////////////////////////////////////////////////////////////////
//! Get the text displayed on the physical key/button associated with an input channel.
//! \param[in] inputChannelId The input channel id whose key or button text to return
//! \param[out] o_keyOrButtonText The text displayed on the physical key/button if found
//! \return True if o_keyOrButtonText was set, false otherwise
virtual bool GetPhysicalKeyOrButtonText(const InputChannelId& /*inputChannelId*/,
AZStd::string& /*o_keyOrButtonText*/) const { return false; }
////////////////////////////////////////////////////////////////////////////////////////
//! Tick/update the input device to broadcast all input events since the last frame
virtual void TickInputDevice() = 0;
////////////////////////////////////////////////////////////////////////////////////////
//! Broadcast an event when the input device connects to the system
void BroadcastInputDeviceConnectedEvent() const;
////////////////////////////////////////////////////////////////////////////////////////
//! Broadcast an event when the input device disconnects from the system
void BroadcastInputDeviceDisconnectedEvent() const;
////////////////////////////////////////////////////////////////////////////////////////
//! Alias for verbose container class
using DigitalButtonIdByBitMaskMap = AZStd::unordered_map<AZ::u32, const InputChannelId*>;
protected:
////////////////////////////////////////////////////////////////////////////////////////
//! Platform agnostic representation of a raw game-pad state
struct RawGamepadState
{
////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] digitalButtonIdsByBitMask A map of digital button ids by bitmask
RawGamepadState(const DigitalButtonIdByBitMaskMap& digitalButtonIdsByBitMask);
////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(RawGamepadState);
////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
~RawGamepadState() = default;
////////////////////////////////////////////////////////////////////////////////////
//! Reset the raw gamepad state
void Reset();
////////////////////////////////////////////////////////////////////////////////////
//! Get the left trigger value adjusted for the dead zone and normalized
//! \return The left trigger value adjusted for the dead zone and normalized
float GetLeftTriggerAdjustedForDeadZoneAndNormalized() const;
////////////////////////////////////////////////////////////////////////////////////
//! Get the right trigger value adjusted for the dead zone and normalized
//! \return The right trigger value adjusted for the dead zone and normalized
float GetRightTriggerAdjustedForDeadZoneAndNormalized() const;
////////////////////////////////////////////////////////////////////////////////////
//! Get the left thumb-stick values adjusted for the dead zone and normalized
//! \return The left thumb-stick values adjusted for the dead zone and normalized
AZ::Vector2 GetLeftThumbStickAdjustedForDeadZoneAndNormalized() const;
////////////////////////////////////////////////////////////////////////////////////
//! Get the right thumb-stick values adjusted for the dead zone and normalized
//! \return The right thumb-stick values adjusted for the dead zone and normalized
AZ::Vector2 GetRightThumbStickAdjustedForDeadZoneAndNormalized() const;
////////////////////////////////////////////////////////////////////////////////////
//! Get the left thumb-stick values normalized with no dead zone applied
//! \return The left thumb-stick values normalized with no dead zone applied
AZ::Vector2 GetLeftThumbStickNormalizedValues() const;
////////////////////////////////////////////////////////////////////////////////////
//! Get the right thumb-stick values normalized with no dead zone applied
//! \return The right thumb-stick values normalized with no dead zone applied
AZ::Vector2 GetRightThumbStickNormalizedValues() const;
////////////////////////////////////////////////////////////////////////////////////
//! The map of digital button ids by bitmask
const DigitalButtonIdByBitMaskMap m_digitalButtonIdsByBitMask;
////////////////////////////////////////////////////////////////////////////////////
// Variables
AZ::u32 m_digitalButtonStates; //!< The state of all digital buttons
float m_triggerButtonLState; //!< The state of the left trigger button
float m_triggerButtonRState; //!< The state of the right trigger button
float m_thumbStickLeftXState; //!< The state of the left thumb-stick x-axis
float m_thumbStickLeftYState; //!< The state of the left thumb-stick y-axis
float m_thumbStickRightXState; //!< The state of the right thumb-stick x-axis
float m_thumbStickRightYState; //!< The state of the right thumb-stick y-axis
float m_triggerMaximumValue; //!< The analog trigger maximum value
float m_triggerDeadZoneValue; //!< The analog trigger dead zone value
float m_thumbStickMaximumValue; //!< The thumb-stick maximum radius value
float m_thumbStickLeftDeadZone; //!< The left thumb-stick radial dead zone value
float m_thumbStickRightDeadZone; //!< The right thumb-stick radial dead zone value
};
////////////////////////////////////////////////////////////////////////////////////////
//! Process a game-pad state that has been obtained since the last call to this function.
//! This function is not thread safe, and so should only be called from the main thread.
//! \param[in] rawGamepadState The raw game-pad state
void ProcessRawGamepadState(const RawGamepadState& rawGamepadState);
////////////////////////////////////////////////////////////////////////////////////////
//! Reset the state of all this input device's associated input channels
void ResetInputChannelStates();
////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDeviceId::GetIndex
AZ::u32 GetInputDeviceIndex() const;
private:
////////////////////////////////////////////////////////////////////////////////////////
// Variables
InputDeviceGamepad& m_inputDevice; //!< Reference to the input device
};
////////////////////////////////////////////////////////////////////////////////////////////
//! Set the implementation of this input device
//! \param[in] implementation The new implementation
void SetImplementation(AZStd::unique_ptr<Implementation> impl) { m_pimpl = AZStd::move(impl); }
private:
////////////////////////////////////////////////////////////////////////////////////////////
//! Private pointer to the platform specific implementation
AZStd::unique_ptr<Implementation> m_pimpl;
////////////////////////////////////////////////////////////////////////////////////////////
//! Helper class that handles requests to create a custom implementation for this device
InputDeviceImplementationRequestHandler<InputDeviceGamepad> m_implementationRequestHandler;
};
} // namespace AzFramework
@@ -0,0 +1,207 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Devices/InputDevice.h>
#include <AzFramework/Input/Buses/Notifications/InputChannelNotificationBus.h>
#include <AzFramework/Input/Buses/Notifications/InputDeviceNotificationBus.h>
#include <AzFramework/Input/Buses/Notifications/InputTextNotificationBus.h>
#include <AzFramework/Input/Buses/Requests/InputChannelRequestBus.h>
#include <AzCore/RTTI/BehaviorContext.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
class InputDeviceNotificationBusBehaviorHandler
: public InputDeviceNotificationBus::Handler
, public AZ::BehaviorEBusHandler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
AZ_EBUS_BEHAVIOR_BINDER(InputDeviceNotificationBusBehaviorHandler
, "{95C1315E-C568-458B-B29F-8FC610B25EF7}"
, AZ::SystemAllocator
, OnInputDeviceConnectedEvent
, OnInputDeviceDisconnectedEvent
);
////////////////////////////////////////////////////////////////////////////////////////////
void OnInputDeviceConnectedEvent(const InputDevice& inputDevice) override
{
Call(FN_OnInputDeviceConnectedEvent, &inputDevice);
}
////////////////////////////////////////////////////////////////////////////////////////////
void OnInputDeviceDisconnectedEvent(const InputDevice& inputDevice)
{
Call(FN_OnInputDeviceDisconnectedEvent, &inputDevice);
}
};
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDevice::Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<InputDevice>()
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::RuntimeOwn)
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Attribute(AZ::Script::Attributes::Category, "Input")
->Property("deviceName", [](InputDevice* thisPtr) { return thisPtr->GetInputDeviceId().GetName(); }, nullptr)
->Property("deviceIndex", [](InputDevice* thisPtr) { return thisPtr->GetInputDeviceId().GetIndex(); }, nullptr)
->Property("localUserId", [](InputDevice* thisPtr) { return thisPtr->GetAssignedLocalUserId(); }, nullptr)
->Method("PromptLocalUserSignIn", &InputDevice::PromptLocalUserSignIn)
->Method("IsSupported", &InputDevice::IsSupported)
->Method("IsConnected", &InputDevice::IsConnected)
;
behaviorContext->EBus<InputDeviceNotificationBus>("InputDeviceNotificationBus")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Attribute(AZ::Script::Attributes::Category, "Input")
->Handler<InputDeviceNotificationBusBehaviorHandler>()
;
behaviorContext->EBus<InputDeviceRequestBus>("InputDeviceRequestBus")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Attribute(AZ::Script::Attributes::Category, "Input")
->Event("GetInputDevice", &InputDeviceRequestBus::Events::GetInputDevice)
;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDevice::InputDevice(const InputDeviceId& inputDeviceId)
: m_inputDeviceId(inputDeviceId)
{
InputDeviceRequestBus::Handler::BusConnect(m_inputDeviceId);
ApplicationLifecycleEvents::Bus::Handler::BusConnect();
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDevice::~InputDevice()
{
ApplicationLifecycleEvents::Bus::Handler::BusDisconnect();
InputDeviceRequestBus::Handler::BusDisconnect(m_inputDeviceId);
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputDevice* InputDevice::GetInputDevice() const
{
return this;
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputDeviceId& InputDevice::GetInputDeviceId() const
{
return m_inputDeviceId;
}
////////////////////////////////////////////////////////////////////////////////////////////////
LocalUserId InputDevice::GetAssignedLocalUserId() const
{
return GetInputDeviceId().GetIndex();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDevice::BroadcastInputChannelEvent(const InputChannel& inputChannel) const
{
bool hasBeenConsumed = false;
InputChannelNotificationBus::Broadcast(
&InputChannelNotifications::OnInputChannelEvent, inputChannel, hasBeenConsumed);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDevice::BroadcastInputTextEvent(const AZStd::string& textUTF8) const
{
bool hasBeenConsumed = false;
InputTextNotificationBus::Broadcast(
&InputTextNotifications::OnInputTextEvent, textUTF8, hasBeenConsumed);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDevice::BroadcastInputDeviceConnectedEvent() const
{
InputDeviceNotificationBus::Broadcast(
&InputDeviceNotifications::OnInputDeviceConnectedEvent, *this);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDevice::BroadcastInputDeviceDisconnectedEvent() const
{
InputDeviceNotificationBus::Broadcast(
&InputDeviceNotifications::OnInputDeviceDisconnectedEvent, *this);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDevice::ResetInputChannelStates()
{
const InputChannelByIdMap& inputChannelsById = GetInputChannelsById();
for (const auto& inputChannelById : inputChannelsById)
{
// We could do this more efficiently using a const_cast:
//const_cast<InputChannel*>(inputChannelById.second)->ResetState();
const InputChannelRequests::BusIdType requestId(inputChannelById.first,
m_inputDeviceId.GetIndex());
InputChannelRequestBus::Event(requestId, &InputChannelRequests::ResetState);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDevice::GetInputDeviceIds(InputDeviceIdSet& o_deviceIds) const
{
o_deviceIds.insert(m_inputDeviceId);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDevice::GetInputDevicesById(InputDeviceByIdMap& o_devicesById) const
{
o_devicesById[m_inputDeviceId] = this;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDevice::GetInputDevicesByIdWithAssignedLocalUserId(InputDeviceByIdMap& o_devicesById,
LocalUserId localUserId) const
{
if (localUserId == GetAssignedLocalUserId())
{
o_devicesById[m_inputDeviceId] = this;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDevice::GetInputChannelIds(InputChannelIdSet& o_channelIds) const
{
const InputChannelByIdMap& inputChannelsById = GetInputChannelsById();
for (const auto& inputChannelById : inputChannelsById)
{
o_channelIds.insert(inputChannelById.first);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDevice::GetInputChannelsById(InputChannelByIdMap& o_channelsById) const
{
const InputChannelByIdMap& inputChannelsById = GetInputChannelsById();
for (const auto& inputChannelById : inputChannelsById)
{
o_channelsById[inputChannelById.first] = inputChannelById.second;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDevice::OnApplicationConstrained(Event /*lastEvent*/)
{
ResetInputChannelStates();
}
} // namespace AzFramework
@@ -0,0 +1,176 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/API/ApplicationAPI.h>
#include <AzFramework/Input/Buses/Requests/InputDeviceRequestBus.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/std/containers/array.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Base class for all input devices. Input devices are responsible for processing raw input and
//! using it to update their associated input channels. Input devices can also be queried at any
//! time to determine their current connected state, or retrieve their associated input channels.
//!
//! While almost every concrete input device that inherits from this class will likely only send
//! input events when running on a specific platform, we should always be able to access all the
//! potentially available input devices and channels in order to display and set input bindings.
//!
//! Derived classes are responsible for:
//! - Creating and maintaining a collection of associated input channels
//! - Overriding GetInputChannelsById to return all associated input channels
//! - Overriding TickInputDevice to process raw input and update input channel states
//! - Overriding IsSupported to return whether the input device is supported by the platform
//! - Overriding IsConnected to return whether the input device is currently connected
//! - Broadcasting events when the input device gets connected or diconnected
//!
//! Ideally, an input device will use TickInputDevice (that gets called once every frame by the
//! system) to process raw input, update all its input channels, and broadcast all input events.
//! However, this may not always be possible, as the state of an input channel cannot always be
//! queried at will, but will rather be sent by the system at a pre-determined time (eg. touch).
//! In these cases, input devices must queue all raw input until TickInputDevice is called, when
//! they can use it to update their input channels and broadcast events at the appropriate time.
class InputDevice : public InputDeviceRequestBus::Handler
, public ApplicationLifecycleEvents::Bus::Handler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputDevice, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputDevice, "{29F9FB6B-15CB-4DB4-9F36-DE7396B82F3D}");
////////////////////////////////////////////////////////////////////////////////////////////
// Reflection
static void Reflect(AZ::ReflectContext* context);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputDeviceId Id of the input device
explicit InputDevice(const InputDeviceId& inputDeviceId);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputDevice);
////////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
~InputDevice() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDeviceRequests::GetInputDevice
const InputDevice* GetInputDevice() const final;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the input device's id
//! \return Id of the input device
const InputDeviceId& GetInputDeviceId() const;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the input device's currently assigned local user id. By default this returns
//! the device index, but can be overridden to return a platform specific user id in order
//! to support platforms where input comes from a specific user rather than just an index.
//! Values are guaranteed to be unique for the local system, but they are otherwise system
//! dependent and a user id may not necessarily persist for the same user between app runs.
//! \return Id of the local user currently assigned to the input device
virtual LocalUserId GetAssignedLocalUserId() const;
////////////////////////////////////////////////////////////////////////////////////////////
//! Prompt a platform-specific local user sign-in request from this device. Should be called
//! at the appropriate times (eg. start screen, local multiplayer lobby / join screen) where
//! input is detected from a specific input device that does not yet have a user id assigned.
//!
//! Please note that on most platforms this will do nothing, and even on platforms where it
//! does it cannot be assumed that the sign-in flow will actually be completed by the user.
virtual void PromptLocalUserSignIn() const {}
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to all input channels associated with this input device
//! \return Map of all input channels (keyed by their id) associated with this input device
virtual const InputChannelByIdMap& GetInputChannelsById() const = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Query whether the device is supported by the current platform. A device can be supported
//! but not currently connected, while a currently connected device will always be supported.
//! \return True if the input device is supported on the current platform, false otherwise
virtual bool IsSupported() const = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Query whether the device is currently connected to the system. A device can be supported
//! but not currently connected, while a currently connected device will always be supported.
//! \return True if the input device is currently connected, false otherwise
virtual bool IsConnected() const = 0;
protected:
////////////////////////////////////////////////////////////////////////////////////////////
//! Broadcast an event when an associated input channel's state or value is updated
//! \param[in] inputChannel The input channel whose state or value was updated
void BroadcastInputChannelEvent(const InputChannel& inputChannel) const;
////////////////////////////////////////////////////////////////////////////////////////////
//! Broadcast an event when unicode text input is generated by an input device
//! \param[in] textUTF8 The text emitted by the input device (encoded using UTF-8)
void BroadcastInputTextEvent(const AZStd::string& textUTF8) const;
////////////////////////////////////////////////////////////////////////////////////////////
//! Broadcast an event when the input device connects to the system
void BroadcastInputDeviceConnectedEvent() const;
////////////////////////////////////////////////////////////////////////////////////////////
//! Broadcast an event when the input device disconnects from the system
void BroadcastInputDeviceDisconnectedEvent() const;
////////////////////////////////////////////////////////////////////////////////////////////
//! Reset the state of all this input device's associated input channels
void ResetInputChannelStates();
private:
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDeviceRequests::GetInputDeviceIds
void GetInputDeviceIds(InputDeviceIdSet& o_deviceIds) const final;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDeviceRequests::GetInputDevicesById
void GetInputDevicesById(InputDeviceByIdMap& o_devicesById) const final;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDeviceRequests::GetInputDevicesByIdWithAssignedLocalUserId
void GetInputDevicesByIdWithAssignedLocalUserId(InputDeviceByIdMap& o_devicesById,
LocalUserId localUserId) const final;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDeviceRequests::GetInputChannelIds
void GetInputChannelIds(InputChannelIdSet& o_channelIds) const final;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDeviceRequests::GetInputChannelsById
void GetInputChannelsById(InputChannelByIdMap& o_channelsById) const final;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::ApplicationLifecycleEvents::OnApplicationConstrained
void OnApplicationConstrained(Event lastEvent) override;
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
const InputDeviceId m_inputDeviceId; //!< Id of the input device
};
} // namespace AzFramework
@@ -0,0 +1,103 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Devices/InputDeviceId.h>
#include <AzCore/RTTI/BehaviorContext.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceId::Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<InputDeviceId>()
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::Value)
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::All)
->Attribute(AZ::Script::Attributes::Category, "Input")
->Constructor<const char*>()
->Constructor<const char*, AZ::u32>()
->Property("name", [](InputDeviceId* thisPtr) { return thisPtr->GetName(); }, nullptr)
->Property("index", BehaviorValueProperty(&InputDeviceId::m_index))
;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceId::InputDeviceId(const char* name, AZ::u32 index)
: m_crc32(name)
, m_index(index)
{
memset(m_name, 0, AZ_ARRAY_SIZE(m_name));
azstrncpy(m_name, NAME_BUFFER_SIZE, name, MAX_NAME_LENGTH);
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceId::InputDeviceId(const InputDeviceId& other)
: m_crc32(other.m_crc32)
, m_index(other.m_index)
{
memset(m_name, 0, AZ_ARRAY_SIZE(m_name));
azstrcpy(m_name, NAME_BUFFER_SIZE, other.m_name);
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceId& InputDeviceId::operator=(const InputDeviceId& other)
{
azstrcpy(m_name, NAME_BUFFER_SIZE, other.m_name);
m_crc32 = other.m_crc32;
m_index = other.m_index;
return *this;
}
////////////////////////////////////////////////////////////////////////////////////////////////
const char* InputDeviceId::GetName() const
{
return m_name;
}
////////////////////////////////////////////////////////////////////////////////////////////////
const AZ::Crc32& InputDeviceId::GetNameCrc32() const
{
return m_crc32;
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZ::u32 InputDeviceId::GetIndex() const
{
return m_index;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceId::operator==(const InputDeviceId& other) const
{
return (m_crc32 == other.m_crc32) && (m_index == other.m_index);
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceId::operator!=(const InputDeviceId& other) const
{
return !(*this == other);
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceId::operator<(const InputDeviceId& other) const
{
if (m_index == other.m_index)
{
return m_crc32 < other.m_crc32;
}
return m_index < other.m_index;
}
} // namespace AzFramework
@@ -0,0 +1,120 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Math/Crc.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/std/hash.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Class that identifies a specific input device
class InputDeviceId
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
// Constants
static const int NAME_BUFFER_SIZE = 64;
static const int MAX_NAME_LENGTH = NAME_BUFFER_SIZE - 1;
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputDeviceId, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_TYPE_INFO(InputDeviceId, "{E58630A4-D380-4289-AA29-83300636A954}");
////////////////////////////////////////////////////////////////////////////////////////////
// Reflection
static void Reflect(AZ::ReflectContext* context);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] name Name of the input device (will be truncated if exceeds MAX_NAME_LENGTH)
//! \param[in] index Index of the input device (optional)
explicit InputDeviceId(const char* name, AZ::u32 index = 0);
////////////////////////////////////////////////////////////////////////////////////////////
//! Copy constructor
//! \param[in] other Another instance of the class to copy from
InputDeviceId(const InputDeviceId& other);
////////////////////////////////////////////////////////////////////////////////////////////
//! Copy assignment operator
//! \param[in] other Another instance of the class to copy from
InputDeviceId& operator=(const InputDeviceId& other);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
~InputDeviceId() = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the input device's name
//! \return Name of the input device
const char* GetName() const;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the crc32 of the input device's name
//! \return crc32 of the input device name
const AZ::Crc32& GetNameCrc32() const;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the input device's index. Used for differentiating between multiple instances
//! of the same device type, regardless of whether the device has a local user id assigned.
//! In some cases the device index and local user are the same, but this cannot be assumed.
//! For example, by default the engine supports up to four gamepad devices that are created
//! at startup using indicies 0->3. As gamepads connect/disconnect at runtime we assign the
//! appropriate (system dependent) local user id (see InputDevice::GetAssignedLocalUserId).
//! \return Index of the input device
AZ::u32 GetIndex() const;
////////////////////////////////////////////////////////////////////////////////////////////
///@{
//! Equality comparison operator
//! \param[in] other Another instance of the class to compare for equality
bool operator==(const InputDeviceId& other) const;
bool operator!=(const InputDeviceId& other) const;
///@}
////////////////////////////////////////////////////////////////////////////////////////////
//! Less than comparison operator
//! \param[in] other Another instance of the class to compare
bool operator<(const InputDeviceId& other) const;
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
char m_name[NAME_BUFFER_SIZE]; //!< Name of the input device
AZ::Crc32 m_crc32; //!< Crc32 of the input device
AZ::u32 m_index; //!< Index of the input device
};
} // namespace AzFramework
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AZStd
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Hash structure specialization for InputDeviceId
template<> struct hash<AzFramework::InputDeviceId>
{
inline size_t operator()(const AzFramework::InputDeviceId& inputDeviceId) const
{
size_t hashValue = inputDeviceId.GetNameCrc32();
AZStd::hash_combine(hashValue, inputDeviceId.GetIndex());
return hashValue;
}
};
} // namespace AZStd
@@ -0,0 +1,621 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Utils/ProcessRawInputEventQueues.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/std/smart_ptr/make_shared.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
const InputDeviceId InputDeviceKeyboard::Id("keyboard");
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceKeyboard::IsKeyboardDevice(const InputDeviceId& inputDeviceId)
{
return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32());
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Alphanumeric Keys
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric0("keyboard_key_alphanumeric_0");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric1("keyboard_key_alphanumeric_1");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric2("keyboard_key_alphanumeric_2");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric3("keyboard_key_alphanumeric_3");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric4("keyboard_key_alphanumeric_4");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric5("keyboard_key_alphanumeric_5");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric6("keyboard_key_alphanumeric_6");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric7("keyboard_key_alphanumeric_7");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric8("keyboard_key_alphanumeric_8");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric9("keyboard_key_alphanumeric_9");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericA("keyboard_key_alphanumeric_A");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericB("keyboard_key_alphanumeric_B");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericC("keyboard_key_alphanumeric_C");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericD("keyboard_key_alphanumeric_D");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericE("keyboard_key_alphanumeric_E");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericF("keyboard_key_alphanumeric_F");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericG("keyboard_key_alphanumeric_G");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericH("keyboard_key_alphanumeric_H");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericI("keyboard_key_alphanumeric_I");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericJ("keyboard_key_alphanumeric_J");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericK("keyboard_key_alphanumeric_K");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericL("keyboard_key_alphanumeric_L");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericM("keyboard_key_alphanumeric_M");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericN("keyboard_key_alphanumeric_N");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericO("keyboard_key_alphanumeric_O");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericP("keyboard_key_alphanumeric_P");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericQ("keyboard_key_alphanumeric_Q");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericR("keyboard_key_alphanumeric_R");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericS("keyboard_key_alphanumeric_S");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericT("keyboard_key_alphanumeric_T");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericU("keyboard_key_alphanumeric_U");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericV("keyboard_key_alphanumeric_V");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericW("keyboard_key_alphanumeric_W");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericX("keyboard_key_alphanumeric_X");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericY("keyboard_key_alphanumeric_Y");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericZ("keyboard_key_alphanumeric_Z");
////////////////////////////////////////////////////////////////////////////////////////////////
// Edit (and escape) Keys
const InputChannelId InputDeviceKeyboard::Key::EditBackspace("keyboard_key_edit_backspace");
const InputChannelId InputDeviceKeyboard::Key::EditCapsLock("keyboard_key_edit_capslock");
const InputChannelId InputDeviceKeyboard::Key::EditEnter("keyboard_key_edit_enter");
const InputChannelId InputDeviceKeyboard::Key::EditSpace("keyboard_key_edit_space");
const InputChannelId InputDeviceKeyboard::Key::EditTab("keyboard_key_edit_tab");
const InputChannelId InputDeviceKeyboard::Key::Escape("keyboard_key_escape");
////////////////////////////////////////////////////////////////////////////////////////////////
// Function Keys
const InputChannelId InputDeviceKeyboard::Key::Function01("keyboard_key_function_F01");
const InputChannelId InputDeviceKeyboard::Key::Function02("keyboard_key_function_F02");
const InputChannelId InputDeviceKeyboard::Key::Function03("keyboard_key_function_F03");
const InputChannelId InputDeviceKeyboard::Key::Function04("keyboard_key_function_F04");
const InputChannelId InputDeviceKeyboard::Key::Function05("keyboard_key_function_F05");
const InputChannelId InputDeviceKeyboard::Key::Function06("keyboard_key_function_F06");
const InputChannelId InputDeviceKeyboard::Key::Function07("keyboard_key_function_F07");
const InputChannelId InputDeviceKeyboard::Key::Function08("keyboard_key_function_F08");
const InputChannelId InputDeviceKeyboard::Key::Function09("keyboard_key_function_F09");
const InputChannelId InputDeviceKeyboard::Key::Function10("keyboard_key_function_F10");
const InputChannelId InputDeviceKeyboard::Key::Function11("keyboard_key_function_F11");
const InputChannelId InputDeviceKeyboard::Key::Function12("keyboard_key_function_F12");
const InputChannelId InputDeviceKeyboard::Key::Function13("keyboard_key_function_F13");
const InputChannelId InputDeviceKeyboard::Key::Function14("keyboard_key_function_F14");
const InputChannelId InputDeviceKeyboard::Key::Function15("keyboard_key_function_F15");
const InputChannelId InputDeviceKeyboard::Key::Function16("keyboard_key_function_F16");
const InputChannelId InputDeviceKeyboard::Key::Function17("keyboard_key_function_F17");
const InputChannelId InputDeviceKeyboard::Key::Function18("keyboard_key_function_F18");
const InputChannelId InputDeviceKeyboard::Key::Function19("keyboard_key_function_F19");
const InputChannelId InputDeviceKeyboard::Key::Function20("keyboard_key_function_F20");
////////////////////////////////////////////////////////////////////////////////////////////////
// Modifier Keys
const InputChannelId InputDeviceKeyboard::Key::ModifierAltL("keyboard_key_modifier_alt_l");
const InputChannelId InputDeviceKeyboard::Key::ModifierAltR("keyboard_key_modifier_alt_r");
const InputChannelId InputDeviceKeyboard::Key::ModifierCtrlL("keyboard_key_modifier_ctrl_l");
const InputChannelId InputDeviceKeyboard::Key::ModifierCtrlR("keyboard_key_modifier_ctrl_r");
const InputChannelId InputDeviceKeyboard::Key::ModifierShiftL("keyboard_key_modifier_shift_l");
const InputChannelId InputDeviceKeyboard::Key::ModifierShiftR("keyboard_key_modifier_shift_r");
const InputChannelId InputDeviceKeyboard::Key::ModifierSuperL("keyboard_key_modifier_super_l");
const InputChannelId InputDeviceKeyboard::Key::ModifierSuperR("keyboard_key_modifier_super_r");
////////////////////////////////////////////////////////////////////////////////////////////////
// Navigation Keys
const InputChannelId InputDeviceKeyboard::Key::NavigationArrowDown("keyboard_key_navigation_arrow_down");
const InputChannelId InputDeviceKeyboard::Key::NavigationArrowLeft("keyboard_key_navigation_arrow_left");
const InputChannelId InputDeviceKeyboard::Key::NavigationArrowRight("keyboard_key_navigation_arrow_right");
const InputChannelId InputDeviceKeyboard::Key::NavigationArrowUp("keyboard_key_navigation_arrow_up");
const InputChannelId InputDeviceKeyboard::Key::NavigationDelete("keyboard_key_navigation_delete");
const InputChannelId InputDeviceKeyboard::Key::NavigationEnd("keyboard_key_navigation_end");
const InputChannelId InputDeviceKeyboard::Key::NavigationHome("keyboard_key_navigation_home");
const InputChannelId InputDeviceKeyboard::Key::NavigationInsert("keyboard_key_navigation_insert");
const InputChannelId InputDeviceKeyboard::Key::NavigationPageDown("keyboard_key_navigation_page_down");
const InputChannelId InputDeviceKeyboard::Key::NavigationPageUp("keyboard_key_navigation_page_up");
////////////////////////////////////////////////////////////////////////////////////////////////
// Numpad Keys
const InputChannelId InputDeviceKeyboard::Key::NumLock("keyboard_key_num_lock");
const InputChannelId InputDeviceKeyboard::Key::NumPad0("keyboard_key_numpad_0");
const InputChannelId InputDeviceKeyboard::Key::NumPad1("keyboard_key_numpad_1");
const InputChannelId InputDeviceKeyboard::Key::NumPad2("keyboard_key_numpad_2");
const InputChannelId InputDeviceKeyboard::Key::NumPad3("keyboard_key_numpad_3");
const InputChannelId InputDeviceKeyboard::Key::NumPad4("keyboard_key_numpad_4");
const InputChannelId InputDeviceKeyboard::Key::NumPad5("keyboard_key_numpad_5");
const InputChannelId InputDeviceKeyboard::Key::NumPad6("keyboard_key_numpad_6");
const InputChannelId InputDeviceKeyboard::Key::NumPad7("keyboard_key_numpad_7");
const InputChannelId InputDeviceKeyboard::Key::NumPad8("keyboard_key_numpad_8");
const InputChannelId InputDeviceKeyboard::Key::NumPad9("keyboard_key_numpad_9");
const InputChannelId InputDeviceKeyboard::Key::NumPadAdd("keyboard_key_numpad_add");
const InputChannelId InputDeviceKeyboard::Key::NumPadDecimal("keyboard_key_numpad_decimal");
const InputChannelId InputDeviceKeyboard::Key::NumPadDivide("keyboard_key_numpad_divide");
const InputChannelId InputDeviceKeyboard::Key::NumPadEnter("keyboard_key_numpad_enter");
const InputChannelId InputDeviceKeyboard::Key::NumPadMultiply("keyboard_key_numpad_multiply");
const InputChannelId InputDeviceKeyboard::Key::NumPadSubtract("keyboard_key_numpad_subtract");
////////////////////////////////////////////////////////////////////////////////////////////////
// Punctuation Keys
const InputChannelId InputDeviceKeyboard::Key::PunctuationApostrophe("keyboard_key_punctuation_apostrophe");
const InputChannelId InputDeviceKeyboard::Key::PunctuationBackslash("keyboard_key_punctuation_backslash");
const InputChannelId InputDeviceKeyboard::Key::PunctuationBracketL("keyboard_key_punctuation_bracket_l");
const InputChannelId InputDeviceKeyboard::Key::PunctuationBracketR("keyboard_key_punctuation_bracket_r");
const InputChannelId InputDeviceKeyboard::Key::PunctuationComma("keyboard_key_punctuation_comma");
const InputChannelId InputDeviceKeyboard::Key::PunctuationEquals("keyboard_key_punctuation_equals");
const InputChannelId InputDeviceKeyboard::Key::PunctuationHyphen("keyboard_key_punctuation_hyphen");
const InputChannelId InputDeviceKeyboard::Key::PunctuationPeriod("keyboard_key_punctuation_period");
const InputChannelId InputDeviceKeyboard::Key::PunctuationSemicolon("keyboard_key_punctuation_semicolon");
const InputChannelId InputDeviceKeyboard::Key::PunctuationSlash("keyboard_key_punctuation_slash");
const InputChannelId InputDeviceKeyboard::Key::PunctuationTilde("keyboard_key_punctuation_tilde");
////////////////////////////////////////////////////////////////////////////////////////////////
// Supplementary ISO Key
const InputChannelId InputDeviceKeyboard::Key::SupplementaryISO("keyboard_key_supplementary_iso");
////////////////////////////////////////////////////////////////////////////////////////////////
// Windows System Keys
const InputChannelId InputDeviceKeyboard::Key::WindowsSystemPause("keyboard_key_windows_system_pause");
const InputChannelId InputDeviceKeyboard::Key::WindowsSystemPrint("keyboard_key_windows_system_print");
const InputChannelId InputDeviceKeyboard::Key::WindowsSystemScrollLock("keyboard_key_windows_system_scroll_lock");
////////////////////////////////////////////////////////////////////////////////////////////////
const AZStd::array<InputChannelId, 112> InputDeviceKeyboard::Key::All =
{{
// Alphanumeric Keys
Alphanumeric0,
Alphanumeric1,
Alphanumeric2,
Alphanumeric3,
Alphanumeric4,
Alphanumeric5,
Alphanumeric6,
Alphanumeric7,
Alphanumeric8,
Alphanumeric9,
AlphanumericA,
AlphanumericB,
AlphanumericC,
AlphanumericD,
AlphanumericE,
AlphanumericF,
AlphanumericG,
AlphanumericH,
AlphanumericI,
AlphanumericJ,
AlphanumericK,
AlphanumericL,
AlphanumericM,
AlphanumericN,
AlphanumericO,
AlphanumericP,
AlphanumericQ,
AlphanumericR,
AlphanumericS,
AlphanumericT,
AlphanumericU,
AlphanumericV,
AlphanumericW,
AlphanumericX,
AlphanumericY,
AlphanumericZ,
// Edit (and escape) Keys
EditBackspace,
EditCapsLock,
EditEnter,
EditSpace,
EditTab,
Escape,
// Function Keys
Function01,
Function02,
Function03,
Function04,
Function05,
Function06,
Function07,
Function08,
Function09,
Function10,
Function11,
Function12,
Function13,
Function14,
Function15,
Function16,
Function17,
Function18,
Function19,
Function20,
// Modifier Keys
ModifierAltL,
ModifierAltR,
ModifierCtrlL,
ModifierCtrlR,
ModifierShiftL,
ModifierShiftR,
ModifierSuperL,
ModifierSuperR,
// Navigation Keys
NavigationArrowDown,
NavigationArrowLeft,
NavigationArrowRight,
NavigationArrowUp,
NavigationDelete,
NavigationEnd,
NavigationHome,
NavigationInsert,
NavigationPageDown,
NavigationPageUp,
// Numpad Keys
NumLock,
NumPad0,
NumPad1,
NumPad2,
NumPad3,
NumPad4,
NumPad5,
NumPad6,
NumPad7,
NumPad8,
NumPad9,
NumPadAdd,
NumPadDecimal,
NumPadDivide,
NumPadEnter,
NumPadMultiply,
NumPadSubtract,
// Punctuation Keys
PunctuationApostrophe,
PunctuationBackslash,
PunctuationBracketL,
PunctuationBracketR,
PunctuationComma,
PunctuationEquals,
PunctuationHyphen,
PunctuationPeriod,
PunctuationSemicolon,
PunctuationSlash,
PunctuationTilde,
// Supplementary ISO Key
SupplementaryISO,
// Windows System Keys
WindowsSystemPause,
WindowsSystemPrint,
WindowsSystemScrollLock
}};
////////////////////////////////////////////////////////////////////////////////////////////////
ModifierKeyMask GetCorrespondingModifierKeyMask(const InputChannelId& channelId)
{
if (channelId == InputDeviceKeyboard::Key::ModifierAltL) { return ModifierKeyMask::AltL; }
if (channelId == InputDeviceKeyboard::Key::ModifierAltR) { return ModifierKeyMask::AltR; }
if (channelId == InputDeviceKeyboard::Key::ModifierCtrlL) { return ModifierKeyMask::CtrlL; }
if (channelId == InputDeviceKeyboard::Key::ModifierCtrlR) { return ModifierKeyMask::CtrlR; }
if (channelId == InputDeviceKeyboard::Key::ModifierShiftL) { return ModifierKeyMask::ShiftL; }
if (channelId == InputDeviceKeyboard::Key::ModifierShiftR) { return ModifierKeyMask::ShiftR; }
if (channelId == InputDeviceKeyboard::Key::ModifierSuperL) { return ModifierKeyMask::SuperL; }
if (channelId == InputDeviceKeyboard::Key::ModifierSuperR) { return ModifierKeyMask::SuperR; }
return ModifierKeyMask::None;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceKeyboard::Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
// Unfortunately it doesn't seem possible to reflect anything through BehaviorContext
// using lambdas which capture variables from the enclosing scope. So we are manually
// reflecting all input channel names, instead of just iterating over them like this:
//
// auto classBuilder = behaviorContext->Class<InputDeviceKeyboard>();
// for (const InputChannelId& channelId : Key::All)
// {
// const char* channelName = channelId.GetName();
// classBuilder->Constant(channelName, [channelName]() { return channelName; });
// }
behaviorContext->Class<InputDeviceKeyboard>()
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::RuntimeOwn)
->Constant("name", BehaviorConstant(Id.GetName()))
->Constant(Key::Alphanumeric0.GetName(), BehaviorConstant(Key::Alphanumeric0.GetName()))
->Constant(Key::Alphanumeric1.GetName(), BehaviorConstant(Key::Alphanumeric1.GetName()))
->Constant(Key::Alphanumeric2.GetName(), BehaviorConstant(Key::Alphanumeric2.GetName()))
->Constant(Key::Alphanumeric3.GetName(), BehaviorConstant(Key::Alphanumeric3.GetName()))
->Constant(Key::Alphanumeric4.GetName(), BehaviorConstant(Key::Alphanumeric4.GetName()))
->Constant(Key::Alphanumeric5.GetName(), BehaviorConstant(Key::Alphanumeric5.GetName()))
->Constant(Key::Alphanumeric6.GetName(), BehaviorConstant(Key::Alphanumeric6.GetName()))
->Constant(Key::Alphanumeric7.GetName(), BehaviorConstant(Key::Alphanumeric7.GetName()))
->Constant(Key::Alphanumeric8.GetName(), BehaviorConstant(Key::Alphanumeric8.GetName()))
->Constant(Key::Alphanumeric9.GetName(), BehaviorConstant(Key::Alphanumeric9.GetName()))
->Constant(Key::AlphanumericA.GetName(), BehaviorConstant(Key::AlphanumericA.GetName()))
->Constant(Key::AlphanumericB.GetName(), BehaviorConstant(Key::AlphanumericB.GetName()))
->Constant(Key::AlphanumericC.GetName(), BehaviorConstant(Key::AlphanumericC.GetName()))
->Constant(Key::AlphanumericD.GetName(), BehaviorConstant(Key::AlphanumericD.GetName()))
->Constant(Key::AlphanumericE.GetName(), BehaviorConstant(Key::AlphanumericE.GetName()))
->Constant(Key::AlphanumericF.GetName(), BehaviorConstant(Key::AlphanumericF.GetName()))
->Constant(Key::AlphanumericG.GetName(), BehaviorConstant(Key::AlphanumericG.GetName()))
->Constant(Key::AlphanumericH.GetName(), BehaviorConstant(Key::AlphanumericH.GetName()))
->Constant(Key::AlphanumericI.GetName(), BehaviorConstant(Key::AlphanumericI.GetName()))
->Constant(Key::AlphanumericJ.GetName(), BehaviorConstant(Key::AlphanumericJ.GetName()))
->Constant(Key::AlphanumericK.GetName(), BehaviorConstant(Key::AlphanumericK.GetName()))
->Constant(Key::AlphanumericL.GetName(), BehaviorConstant(Key::AlphanumericL.GetName()))
->Constant(Key::AlphanumericM.GetName(), BehaviorConstant(Key::AlphanumericM.GetName()))
->Constant(Key::AlphanumericN.GetName(), BehaviorConstant(Key::AlphanumericN.GetName()))
->Constant(Key::AlphanumericO.GetName(), BehaviorConstant(Key::AlphanumericO.GetName()))
->Constant(Key::AlphanumericP.GetName(), BehaviorConstant(Key::AlphanumericP.GetName()))
->Constant(Key::AlphanumericQ.GetName(), BehaviorConstant(Key::AlphanumericQ.GetName()))
->Constant(Key::AlphanumericR.GetName(), BehaviorConstant(Key::AlphanumericR.GetName()))
->Constant(Key::AlphanumericS.GetName(), BehaviorConstant(Key::AlphanumericS.GetName()))
->Constant(Key::AlphanumericT.GetName(), BehaviorConstant(Key::AlphanumericT.GetName()))
->Constant(Key::AlphanumericU.GetName(), BehaviorConstant(Key::AlphanumericU.GetName()))
->Constant(Key::AlphanumericV.GetName(), BehaviorConstant(Key::AlphanumericV.GetName()))
->Constant(Key::AlphanumericW.GetName(), BehaviorConstant(Key::AlphanumericW.GetName()))
->Constant(Key::AlphanumericX.GetName(), BehaviorConstant(Key::AlphanumericX.GetName()))
->Constant(Key::AlphanumericY.GetName(), BehaviorConstant(Key::AlphanumericY.GetName()))
->Constant(Key::AlphanumericZ.GetName(), BehaviorConstant(Key::AlphanumericZ.GetName()))
->Constant(Key::EditBackspace.GetName(), BehaviorConstant(Key::EditBackspace.GetName()))
->Constant(Key::EditCapsLock.GetName(), BehaviorConstant(Key::EditCapsLock.GetName()))
->Constant(Key::EditEnter.GetName(), BehaviorConstant(Key::EditEnter.GetName()))
->Constant(Key::EditSpace.GetName(), BehaviorConstant(Key::EditSpace.GetName()))
->Constant(Key::EditTab.GetName(), BehaviorConstant(Key::EditTab.GetName()))
->Constant(Key::Escape.GetName(), BehaviorConstant(Key::Escape.GetName()))
->Constant(Key::Function01.GetName(), BehaviorConstant(Key::Function01.GetName()))
->Constant(Key::Function02.GetName(), BehaviorConstant(Key::Function02.GetName()))
->Constant(Key::Function03.GetName(), BehaviorConstant(Key::Function03.GetName()))
->Constant(Key::Function04.GetName(), BehaviorConstant(Key::Function04.GetName()))
->Constant(Key::Function05.GetName(), BehaviorConstant(Key::Function05.GetName()))
->Constant(Key::Function06.GetName(), BehaviorConstant(Key::Function06.GetName()))
->Constant(Key::Function07.GetName(), BehaviorConstant(Key::Function07.GetName()))
->Constant(Key::Function08.GetName(), BehaviorConstant(Key::Function08.GetName()))
->Constant(Key::Function09.GetName(), BehaviorConstant(Key::Function09.GetName()))
->Constant(Key::Function10.GetName(), BehaviorConstant(Key::Function10.GetName()))
->Constant(Key::Function11.GetName(), BehaviorConstant(Key::Function11.GetName()))
->Constant(Key::Function12.GetName(), BehaviorConstant(Key::Function12.GetName()))
->Constant(Key::Function13.GetName(), BehaviorConstant(Key::Function13.GetName()))
->Constant(Key::Function14.GetName(), BehaviorConstant(Key::Function14.GetName()))
->Constant(Key::Function15.GetName(), BehaviorConstant(Key::Function15.GetName()))
->Constant(Key::Function16.GetName(), BehaviorConstant(Key::Function16.GetName()))
->Constant(Key::Function17.GetName(), BehaviorConstant(Key::Function17.GetName()))
->Constant(Key::Function18.GetName(), BehaviorConstant(Key::Function18.GetName()))
->Constant(Key::Function19.GetName(), BehaviorConstant(Key::Function19.GetName()))
->Constant(Key::Function20.GetName(), BehaviorConstant(Key::Function20.GetName()))
->Constant(Key::ModifierAltL.GetName(), BehaviorConstant(Key::ModifierAltL.GetName()))
->Constant(Key::ModifierAltR.GetName(), BehaviorConstant(Key::ModifierAltR.GetName()))
->Constant(Key::ModifierCtrlL.GetName(), BehaviorConstant(Key::ModifierCtrlL.GetName()))
->Constant(Key::ModifierCtrlR.GetName(), BehaviorConstant(Key::ModifierCtrlR.GetName()))
->Constant(Key::ModifierShiftL.GetName(), BehaviorConstant(Key::ModifierShiftL.GetName()))
->Constant(Key::ModifierShiftR.GetName(), BehaviorConstant(Key::ModifierShiftR.GetName()))
->Constant(Key::ModifierSuperL.GetName(), BehaviorConstant(Key::ModifierSuperL.GetName()))
->Constant(Key::ModifierSuperR.GetName(), BehaviorConstant(Key::ModifierSuperR.GetName()))
->Constant(Key::NavigationArrowDown.GetName(), BehaviorConstant(Key::NavigationArrowDown.GetName()))
->Constant(Key::NavigationArrowLeft.GetName(), BehaviorConstant(Key::NavigationArrowLeft.GetName()))
->Constant(Key::NavigationArrowRight.GetName(), BehaviorConstant(Key::NavigationArrowRight.GetName()))
->Constant(Key::NavigationArrowUp.GetName(), BehaviorConstant(Key::NavigationArrowUp.GetName()))
->Constant(Key::NavigationDelete.GetName(), BehaviorConstant(Key::NavigationDelete.GetName()))
->Constant(Key::NavigationEnd.GetName(), BehaviorConstant(Key::NavigationEnd.GetName()))
->Constant(Key::NavigationHome.GetName(), BehaviorConstant(Key::NavigationHome.GetName()))
->Constant(Key::NavigationInsert.GetName(), BehaviorConstant(Key::NavigationInsert.GetName()))
->Constant(Key::NavigationPageDown.GetName(), BehaviorConstant(Key::NavigationPageDown.GetName()))
->Constant(Key::NavigationPageUp.GetName(), BehaviorConstant(Key::NavigationPageUp.GetName()))
->Constant(Key::NumLock.GetName(), BehaviorConstant(Key::NumLock.GetName()))
->Constant(Key::NumPad0.GetName(), BehaviorConstant(Key::NumPad0.GetName()))
->Constant(Key::NumPad1.GetName(), BehaviorConstant(Key::NumPad1.GetName()))
->Constant(Key::NumPad2.GetName(), BehaviorConstant(Key::NumPad2.GetName()))
->Constant(Key::NumPad3.GetName(), BehaviorConstant(Key::NumPad3.GetName()))
->Constant(Key::NumPad4.GetName(), BehaviorConstant(Key::NumPad4.GetName()))
->Constant(Key::NumPad5.GetName(), BehaviorConstant(Key::NumPad5.GetName()))
->Constant(Key::NumPad6.GetName(), BehaviorConstant(Key::NumPad6.GetName()))
->Constant(Key::NumPad7.GetName(), BehaviorConstant(Key::NumPad7.GetName()))
->Constant(Key::NumPad8.GetName(), BehaviorConstant(Key::NumPad8.GetName()))
->Constant(Key::NumPad9.GetName(), BehaviorConstant(Key::NumPad9.GetName()))
->Constant(Key::NumPadAdd.GetName(), BehaviorConstant(Key::NumPadAdd.GetName()))
->Constant(Key::NumPadDecimal.GetName(), BehaviorConstant(Key::NumPadDecimal.GetName()))
->Constant(Key::NumPadDivide.GetName(), BehaviorConstant(Key::NumPadDivide.GetName()))
->Constant(Key::NumPadEnter.GetName(), BehaviorConstant(Key::NumPadEnter.GetName()))
->Constant(Key::NumPadMultiply.GetName(), BehaviorConstant(Key::NumPadMultiply.GetName()))
->Constant(Key::NumPadSubtract.GetName(), BehaviorConstant(Key::NumPadSubtract.GetName()))
->Constant(Key::PunctuationApostrophe.GetName(), BehaviorConstant(Key::PunctuationApostrophe.GetName()))
->Constant(Key::PunctuationBackslash.GetName(), BehaviorConstant(Key::PunctuationBackslash.GetName()))
->Constant(Key::PunctuationBracketL.GetName(), BehaviorConstant(Key::PunctuationBracketL.GetName()))
->Constant(Key::PunctuationBracketR.GetName(), BehaviorConstant(Key::PunctuationBracketR.GetName()))
->Constant(Key::PunctuationComma.GetName(), BehaviorConstant(Key::PunctuationComma.GetName()))
->Constant(Key::PunctuationEquals.GetName(), BehaviorConstant(Key::PunctuationEquals.GetName()))
->Constant(Key::PunctuationHyphen.GetName(), BehaviorConstant(Key::PunctuationHyphen.GetName()))
->Constant(Key::PunctuationPeriod.GetName(), BehaviorConstant(Key::PunctuationPeriod.GetName()))
->Constant(Key::PunctuationSemicolon.GetName(), BehaviorConstant(Key::PunctuationSemicolon.GetName()))
->Constant(Key::PunctuationSlash.GetName(), BehaviorConstant(Key::PunctuationSlash.GetName()))
->Constant(Key::PunctuationTilde.GetName(), BehaviorConstant(Key::PunctuationTilde.GetName()))
->Constant(Key::SupplementaryISO.GetName(), BehaviorConstant(Key::SupplementaryISO.GetName()))
->Constant(Key::WindowsSystemPause.GetName(), BehaviorConstant(Key::WindowsSystemPause.GetName()))
->Constant(Key::WindowsSystemPrint.GetName(), BehaviorConstant(Key::WindowsSystemPrint.GetName()))
->Constant(Key::WindowsSystemScrollLock.GetName(), BehaviorConstant(Key::WindowsSystemScrollLock.GetName()))
;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceKeyboard::InputDeviceKeyboard()
: InputDevice(Id)
, m_modifierKeyStates(AZStd::make_shared<ModifierKeyStates>())
, m_allChannelsById()
, m_keyChannelsById()
, m_pimpl()
, m_implementationRequestHandler(*this)
{
// Create all key input channels
for (const InputChannelId& channelId : Key::All)
{
const ModifierKeyMask modifierKeyMask = GetCorrespondingModifierKeyMask(channelId);
InputChannelDigitalWithSharedModifierKeyStates* channel =
aznew InputChannelDigitalWithSharedModifierKeyStates(channelId,
*this,
m_modifierKeyStates,
modifierKeyMask);
m_allChannelsById[channelId] = channel;
m_keyChannelsById[channelId] = channel;
}
// Create the platform specific implementation
m_pimpl.reset(Implementation::Create(*this));
// Connect to the text entry request bus
InputTextEntryRequestBus::Handler::BusConnect(GetInputDeviceId());
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceKeyboard::~InputDeviceKeyboard()
{
// Disconnect from the text entry request bus
InputTextEntryRequestBus::Handler::BusDisconnect(GetInputDeviceId());
// Destroy the platform specific implementation
m_pimpl.reset();
// Destroy all key input channels
for (const auto& channelById : m_keyChannelsById)
{
delete channelById.second;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
LocalUserId InputDeviceKeyboard::GetAssignedLocalUserId() const
{
return m_pimpl ? m_pimpl->GetAssignedLocalUserId() : InputDevice::GetAssignedLocalUserId();
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputDevice::InputChannelByIdMap& InputDeviceKeyboard::GetInputChannelsById() const
{
return m_allChannelsById;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceKeyboard::IsSupported() const
{
return m_pimpl != nullptr;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceKeyboard::IsConnected() const
{
return m_pimpl ? m_pimpl->IsConnected() : false;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceKeyboard::HasTextEntryStarted() const
{
return m_pimpl ? m_pimpl->HasTextEntryStarted() : false;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceKeyboard::TextEntryStart(const VirtualKeyboardOptions& options)
{
if (m_pimpl)
{
m_pimpl->TextEntryStart(options);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceKeyboard::TextEntryStop()
{
if (m_pimpl)
{
m_pimpl->TextEntryStop();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceKeyboard::TickInputDevice()
{
if (m_pimpl)
{
m_pimpl->TickInputDevice();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceKeyboard::GetPhysicalKeyOrButtonText(const InputChannelId& inputChannelId,
AZStd::string& o_keyOrButtonText) const
{
if (m_pimpl)
{
m_pimpl->GetPhysicalKeyOrButtonText(inputChannelId, o_keyOrButtonText);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceKeyboard::Implementation::Implementation(InputDeviceKeyboard& inputDevice)
: m_inputDevice(inputDevice)
, m_rawKeyEventQueuesById()
, m_rawTextEventQueue()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceKeyboard::Implementation::~Implementation()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
LocalUserId InputDeviceKeyboard::Implementation::GetAssignedLocalUserId() const
{
return m_inputDevice.GetInputDeviceId().GetIndex();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceKeyboard::Implementation::QueueRawKeyEvent(const InputChannelId& inputChannelId,
bool rawKeyState)
{
// It should not (in theory) be possible to receive multiple raw key events with the same id
// and state in succession; if it happens in practice for whatever reason this is still safe.
m_rawKeyEventQueuesById[inputChannelId].push_back(rawKeyState);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceKeyboard::Implementation::QueueRawTextEvent(const AZStd::string& textUTF8)
{
m_rawTextEventQueue.push_back(textUTF8);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceKeyboard::Implementation::ProcessRawEventQueues()
{
// Process all raw input events that were queued since the last call to this function.
// Text events should be processed first in case text input is disabled by a key event.
ProcessRawInputTextEventQueue(m_rawTextEventQueue);
ProcessRawInputEventQueues(m_rawKeyEventQueuesById, m_inputDevice.m_keyChannelsById);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceKeyboard::Implementation::ResetInputChannelStates()
{
m_inputDevice.ResetInputChannelStates();
}
} // namespace AzFramework
@@ -0,0 +1,415 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Buses/Requests/InputTextEntryRequestBus.h>
#include <AzFramework/Input/Channels/InputChannelDigitalWithSharedModifierKeyStates.h>
#include <AzFramework/Input/Devices/InputDevice.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
// Ideally, we would only dispatch text input while it has been explicitly enabled by a call to
// TextEntryStart (paired with a call to TextEntryStop), but to maintain compatibility with the
// existing behavior we must always dispatch keyboard text input by default. Remove this define
// if you want to control text input event dispatch using TextEntryStart and TextEntryStop.
#define ALWAYS_DISPATCH_KEYBOARD_TEXT_INPUT
////////////////////////////////////////////////////////////////////////////////////////////////
//! Defines a generic keyboard input device, including the ids of all associated input channels.
//! Platform specific implementations are defined as private implementations so that creating an
//! instance of this generic class will work correctly on any platform supporting keyboard input,
//! while providing access to the device name and associated channel ids on any platform through
//! the 'null' implementation (primarily so that the editor can use them to setup input mappings).
class InputDeviceKeyboard : public InputDevice
, public InputTextEntryRequestBus::Handler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! The id used to identify the primary physical keyboard input device
static const InputDeviceId Id;
////////////////////////////////////////////////////////////////////////////////////////////
//! Check whether an input device id identifies a physical keyboard (regardless of index)
//! \param[in] inputDeviceId The input device id to check
//! \return True if the input device id identifies a physical keyboard, false otherwise
static bool IsKeyboardDevice(const InputDeviceId& inputDeviceId);
////////////////////////////////////////////////////////////////////////////////////////////
//! All the input channel ids that identify standard physical keyboard keys intended for use
//! as gameplay controls (not virtual keys or ascii/unicode keycodes). They are grouped into
//! categories (roughly based on their physical location and their standard use) as follows.
//!
//! Note that all these key ids correspond to the physical keys of an ANSI mechanical layout
//! as marked using the standard QWERTY visual layout, except for the ISOAdditional id which
//! corresponds to the additional key (next to left-shift) present on ISO mechanical layouts.
//! The additional keys found on keyboards that use JIS mechanical layouts are not supported.
//!
//! Alphanumeric Keys
//! - The A-Z and 0-9 keys
//! - Present on almost all types of physical keyboards
//!
//! Edit (and escape) Keys
//! - The backspace, caps lock, enter, space, tab, and escape keys
//! - Present on almost all types of physical keyboards
//!
//! Function Keys
//! - The F1-F12 keys are present on almost all types of physical keyboards
//! - The F13-F20 keys are only present on some physical keyboards, so their use
//! should be avoided if you wish to supprt the widest range of keyboard devices
//!
//! Modifier Keys
//! - The (left and right) alt, control, shift, and 'super' (windows/apple) keys
//! - Present on almost all types of physical keyboards
//!
//! Navigation Keys
//! - The arrow, delete, insert, home, end, and page up/down keys
//! - Not always present on smaller (eg. laptop) keyboards
//! - Their use should be avoided if you wish to supprt the widest range of keyboard devices
//!
//! Numpad Keys
//! - The various number pad (or keypad) keys, including num lock
//! - Not always present on smaller (eg. laptop) keyboards
//! - These ids will be used regardless of whether the num lock key is active
//! - Their use should be avoided if you wish to supprt the widest range of keyboard devices
//!
//! Punctuation Keys
//! - The various punctuation character keys (eg. comma, period, slash)
//! - Present on almost all types of physical keyboards
//! - Not generally used as input for games
//!
//! Supplementary ISO Key
//! - The additional key (found to the right of the left-shift key) on ISO keyboards
//! - Its use should be avoided if you wish to support the widest range of keyboard devices
//!
//! Windows System Keys
//! - The windows specific pause/break, print/sysrq, and scroll lock keys
//! - Their use should be avoided if you wish to supprt the widest range of keyboard devices
struct Key
{
// Alphanumeric Keys
static const InputChannelId Alphanumeric0; //!< The 0 key
static const InputChannelId Alphanumeric1; //!< The 1 key
static const InputChannelId Alphanumeric2; //!< The 2 key
static const InputChannelId Alphanumeric3; //!< The 3 key
static const InputChannelId Alphanumeric4; //!< The 4 key
static const InputChannelId Alphanumeric5; //!< The 5 key
static const InputChannelId Alphanumeric6; //!< The 6 key
static const InputChannelId Alphanumeric7; //!< The 7 key
static const InputChannelId Alphanumeric8; //!< The 8 key
static const InputChannelId Alphanumeric9; //!< The 9 key
static const InputChannelId AlphanumericA; //!< The A key
static const InputChannelId AlphanumericB; //!< The B key
static const InputChannelId AlphanumericC; //!< The C key
static const InputChannelId AlphanumericD; //!< The D key
static const InputChannelId AlphanumericE; //!< The E key
static const InputChannelId AlphanumericF; //!< The F key
static const InputChannelId AlphanumericG; //!< The G key
static const InputChannelId AlphanumericH; //!< The H key
static const InputChannelId AlphanumericI; //!< The I key
static const InputChannelId AlphanumericJ; //!< The J key
static const InputChannelId AlphanumericK; //!< The K key
static const InputChannelId AlphanumericL; //!< The L key
static const InputChannelId AlphanumericM; //!< The M key
static const InputChannelId AlphanumericN; //!< The N key
static const InputChannelId AlphanumericO; //!< The O key
static const InputChannelId AlphanumericP; //!< The P key
static const InputChannelId AlphanumericQ; //!< The Q key
static const InputChannelId AlphanumericR; //!< The R key
static const InputChannelId AlphanumericS; //!< The S key
static const InputChannelId AlphanumericT; //!< The T key
static const InputChannelId AlphanumericU; //!< The U key
static const InputChannelId AlphanumericV; //!< The V key
static const InputChannelId AlphanumericW; //!< The W key
static const InputChannelId AlphanumericX; //!< The X key
static const InputChannelId AlphanumericY; //!< The Y key
static const InputChannelId AlphanumericZ; //!< The Z key
// Edit (and escape) Keys
static const InputChannelId EditBackspace; //!< The backspace key
static const InputChannelId EditCapsLock; //!< The caps lock key
static const InputChannelId EditEnter; //!< The enter/return key
static const InputChannelId EditSpace; //!< The spacebar key
static const InputChannelId EditTab; //!< The tab key
static const InputChannelId Escape; //!< The escape key
// Function Keys
static const InputChannelId Function01; //!< The F1 key
static const InputChannelId Function02; //!< The F2 key
static const InputChannelId Function03; //!< The F3 key
static const InputChannelId Function04; //!< The F4 key
static const InputChannelId Function05; //!< The F5 key
static const InputChannelId Function06; //!< The F6 key
static const InputChannelId Function07; //!< The F7 key
static const InputChannelId Function08; //!< The F8 key
static const InputChannelId Function09; //!< The F9 key
static const InputChannelId Function10; //!< The F10 key
static const InputChannelId Function11; //!< The F11 key
static const InputChannelId Function12; //!< The F12 key
static const InputChannelId Function13; //!< The F13 key
static const InputChannelId Function14; //!< The F14 key
static const InputChannelId Function15; //!< The F15 key
static const InputChannelId Function16; //!< The F16 key
static const InputChannelId Function17; //!< The F17 key
static const InputChannelId Function18; //!< The F18 key
static const InputChannelId Function19; //!< The F19 key
static const InputChannelId Function20; //!< The F20 key
// Modifier Keys
static const InputChannelId ModifierAltL; //!< The left alt/option key
static const InputChannelId ModifierAltR; //!< The right alt/option key
static const InputChannelId ModifierCtrlL; //!< The left control key
static const InputChannelId ModifierCtrlR; //!< The right control key
static const InputChannelId ModifierShiftL; //!< The left shift key
static const InputChannelId ModifierShiftR; //!< The right shift key
static const InputChannelId ModifierSuperL; //!< The left super (windows or apple) key
static const InputChannelId ModifierSuperR; //!< The right super (windows or apple) key
// Navigation Keys
static const InputChannelId NavigationArrowDown; //!< The down arrow key
static const InputChannelId NavigationArrowLeft; //!< The left arrow key
static const InputChannelId NavigationArrowRight; //!< The right arrow key
static const InputChannelId NavigationArrowUp; //!< The up arrow key
static const InputChannelId NavigationDelete; //!< The delete key
static const InputChannelId NavigationEnd; //!< The end key
static const InputChannelId NavigationHome; //!< The home key
static const InputChannelId NavigationInsert; //!< The insert key
static const InputChannelId NavigationPageDown; //!< The page down key
static const InputChannelId NavigationPageUp; //!< The page up key
// Numpad Keys
static const InputChannelId NumLock; //!< The num lock key (the clear key on apple keyboards)
static const InputChannelId NumPad0; //!< The numpad 0 key
static const InputChannelId NumPad1; //!< The numpad 1 key
static const InputChannelId NumPad2; //!< The numpad 2 key
static const InputChannelId NumPad3; //!< The numpad 3 key
static const InputChannelId NumPad4; //!< The numpad 4 key
static const InputChannelId NumPad5; //!< The numpad 5 key
static const InputChannelId NumPad6; //!< The numpad 6 key
static const InputChannelId NumPad7; //!< The numpad 7 key
static const InputChannelId NumPad8; //!< The numpad 8 key
static const InputChannelId NumPad9; //!< The numpad 9 key
static const InputChannelId NumPadAdd; //!< The numpad add key
static const InputChannelId NumPadDecimal; //!< The numpad decimal key
static const InputChannelId NumPadDivide; //!< The numpad divide key
static const InputChannelId NumPadEnter; //!< The numpad enter key
static const InputChannelId NumPadMultiply; //!< The numpad multiply key
static const InputChannelId NumPadSubtract; //!< The numpad subtract key
// Punctuation Keys
static const InputChannelId PunctuationApostrophe; //!< The apostrophe key
static const InputChannelId PunctuationBackslash; //!< The backslash key
static const InputChannelId PunctuationBracketL; //!< The left bracket key
static const InputChannelId PunctuationBracketR; //!< The right bracket key
static const InputChannelId PunctuationComma; //!< The comma key
static const InputChannelId PunctuationEquals; //!< The equals key
static const InputChannelId PunctuationHyphen; //!< The hyphen/underscore key
static const InputChannelId PunctuationPeriod; //!< The period key
static const InputChannelId PunctuationSemicolon; //!< The semicolon key
static const InputChannelId PunctuationSlash; //!< The (forward) slash key
static const InputChannelId PunctuationTilde; //!< The tilde/grave key
// Supplementary ISO Key
static const InputChannelId SupplementaryISO; //!< The supplementary ISO layout key
// Windows System Keys
static const InputChannelId WindowsSystemPause; //!< The windows pause key
static const InputChannelId WindowsSystemPrint; //!< The windows print key
static const InputChannelId WindowsSystemScrollLock; //!< The windows scroll lock key
//!< All keyboard key ids
static const AZStd::array<InputChannelId, 112> All;
};
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputDeviceKeyboard, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputDeviceKeyboard, "{CFD40F74-81DF-40B1-995B-F7142E6B1259}", InputDevice);
////////////////////////////////////////////////////////////////////////////////////////////
// Reflection
static void Reflect(AZ::ReflectContext* context);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
InputDeviceKeyboard();
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputDeviceKeyboard);
////////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
~InputDeviceKeyboard() override;
////////////////////////////////////////////////////////////////////////////////////////////
LocalUserId GetAssignedLocalUserId() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::GetInputChannelsById
const InputChannelByIdMap& GetInputChannelsById() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::IsSupported
bool IsSupported() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::IsConnected
bool IsConnected() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputTextEntryRequests::HasTextEntryStarted
bool HasTextEntryStarted() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputTextEntryRequests::TextEntryStart
void TextEntryStart(const VirtualKeyboardOptions& options) override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputTextEntryRequests::TextEntryStop
void TextEntryStop() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDeviceRequests::TickInputDevice
void TickInputDevice() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDeviceRequests::GetPhysicalKeyOrButtonText
void GetPhysicalKeyOrButtonText(const InputChannelId& inputChannelId,
AZStd::string& o_keyOrButtonText) const override;
protected:
////////////////////////////////////////////////////////////////////////////////////////////
//! Alias for verbose container class
using KeyChannelByIdMap = AZStd::unordered_map<InputChannelId, InputChannelDigitalWithSharedModifierKeyStates*>;
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
SharedModifierKeyStates m_modifierKeyStates; //!< Shared modifier key states
InputChannelByIdMap m_allChannelsById; //!< All keyboard channels by id
KeyChannelByIdMap m_keyChannelsById; //!< All keyboard key channels by id
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Base class for platform specific implementations of keyboard input devices
class Implementation
{
public:
////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(Implementation, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////
//! Default factory create function
//! \param[in] inputDevice Reference to the input device being implemented
static Implementation* Create(InputDeviceKeyboard& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputDevice Reference to the input device being implemented
Implementation(InputDeviceKeyboard& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(Implementation);
////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
virtual ~Implementation();
////////////////////////////////////////////////////////////////////////////////////////
virtual LocalUserId GetAssignedLocalUserId() const;
////////////////////////////////////////////////////////////////////////////////////////
//! Query the connected state of the input device
//! \return True if the input device is currently connected, false otherwise
virtual bool IsConnected() const = 0;
////////////////////////////////////////////////////////////////////////////////////////
//! Query whether text entry has already been started
//! \return True if text entry has already been started, false otherwise
virtual bool HasTextEntryStarted() const = 0;
////////////////////////////////////////////////////////////////////////////////////////
//! Inform input device that text input is expected to start (pair with StopTextInput)
//! \param[in] options Used to specify the appearance/behavior of any virtual keyboard
virtual void TextEntryStart(const VirtualKeyboardOptions& options) = 0;
////////////////////////////////////////////////////////////////////////////////////////
//! Inform input device that text input is expected to stop (pair with StartTextInput)
virtual void TextEntryStop() = 0;
////////////////////////////////////////////////////////////////////////////////////////
//! Tick/update the input device to broadcast all input events since the last frame
virtual void TickInputDevice() = 0;
////////////////////////////////////////////////////////////////////////////////////////
//! Get the text displayed on the physical key/button associated with an input channel.
//! In the case of keyboard keys, we must take into account the current keyboard layout.
//! \param[in] inputChannelId The input channel id whose key or button text to return
//! \param[out] o_keyOrButtonText The text displayed on the physical key/button if found
virtual void GetPhysicalKeyOrButtonText(const InputChannelId& /*inputChannelId*/,
AZStd::string& /*o_keyOrButtonText*/) const {}
protected:
////////////////////////////////////////////////////////////////////////////////////////
//! Queue raw key events to be processed in the next call to ProcessRawEventQueues.
//! This function is not thread safe and so should only be called from the main thread.
//! \param[in] inputChannelId The input channel id
//! \param[in] rawKeyState The raw key state
void QueueRawKeyEvent(const InputChannelId& inputChannelId, bool rawKeyState);
////////////////////////////////////////////////////////////////////////////////////////
//! Queue raw text events to be processed in the next call to ProcessRawEventQueues.
//! This function is not thread safe and so should only be called from the main thread.
//! \param[in] textUTF8 The text to queue (encoded using UTF-8)
void QueueRawTextEvent(const AZStd::string& textUTF8);
////////////////////////////////////////////////////////////////////////////////////////
//! Process raw input events that have been queued since the last call to this function.
//! This function is not thread safe, and so should only be called from the main thread.
void ProcessRawEventQueues();
////////////////////////////////////////////////////////////////////////////////////////
//! Reset the state of all this input device's associated input channels
void ResetInputChannelStates();
////////////////////////////////////////////////////////////////////////////////////////
//! Alias for verbose container class
using RawKeyEventQueueByIdMap = AZStd::unordered_map<InputChannelId, AZStd::vector<bool>>;
////////////////////////////////////////////////////////////////////////////////////////
// Variables
InputDeviceKeyboard& m_inputDevice; //!< Reference to the input device
RawKeyEventQueueByIdMap m_rawKeyEventQueuesById; //!< Raw key event queues by id
AZStd::vector<AZStd::string> m_rawTextEventQueue; //!< Raw text event queue
};
////////////////////////////////////////////////////////////////////////////////////////////
//! Set the implementation of this input device
//! \param[in] implementation The new implementation
void SetImplementation(AZStd::unique_ptr<Implementation> impl) { m_pimpl = AZStd::move(impl); }
private:
////////////////////////////////////////////////////////////////////////////////////////////
//! Private pointer to the platform specific implementation
AZStd::unique_ptr<Implementation> m_pimpl;
////////////////////////////////////////////////////////////////////////////////////////////
//! Helper class that handles requests to create a custom implementation for this device
InputDeviceImplementationRequestHandler<InputDeviceKeyboard> m_implementationRequestHandler;
};
} // namespace AzFramework
@@ -0,0 +1,584 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace
{
using namespace AzFramework;
////////////////////////////////////////////////////////////////////////////////////////////////
// Table of key ids indexed by their windows scan code if the E0 'extended bit' prefix isn't set
const AZStd::array<const InputChannelId*, 128> InputChannelIdByScanCodeTable =
{{
nullptr, // 0x00
&InputDeviceKeyboard::Key::Escape, // 0x01
&InputDeviceKeyboard::Key::Alphanumeric1, // 0x02
&InputDeviceKeyboard::Key::Alphanumeric2, // 0x03
&InputDeviceKeyboard::Key::Alphanumeric3, // 0x04
&InputDeviceKeyboard::Key::Alphanumeric4, // 0x05
&InputDeviceKeyboard::Key::Alphanumeric5, // 0x06
&InputDeviceKeyboard::Key::Alphanumeric6, // 0x07
&InputDeviceKeyboard::Key::Alphanumeric7, // 0x08
&InputDeviceKeyboard::Key::Alphanumeric8, // 0x09
&InputDeviceKeyboard::Key::Alphanumeric9, // 0x0A
&InputDeviceKeyboard::Key::Alphanumeric0, // 0x0B
&InputDeviceKeyboard::Key::PunctuationHyphen, // 0x0C
&InputDeviceKeyboard::Key::PunctuationEquals, // 0x0D
&InputDeviceKeyboard::Key::EditBackspace, // 0x0E
&InputDeviceKeyboard::Key::EditTab, // 0x0F
&InputDeviceKeyboard::Key::AlphanumericQ, // 0x10
&InputDeviceKeyboard::Key::AlphanumericW, // 0x11
&InputDeviceKeyboard::Key::AlphanumericE, // 0x12
&InputDeviceKeyboard::Key::AlphanumericR, // 0x13
&InputDeviceKeyboard::Key::AlphanumericT, // 0x14
&InputDeviceKeyboard::Key::AlphanumericY, // 0x15
&InputDeviceKeyboard::Key::AlphanumericU, // 0x16
&InputDeviceKeyboard::Key::AlphanumericI, // 0x17
&InputDeviceKeyboard::Key::AlphanumericO, // 0x18
&InputDeviceKeyboard::Key::AlphanumericP, // 0x19
&InputDeviceKeyboard::Key::PunctuationBracketL, // 0x1A
&InputDeviceKeyboard::Key::PunctuationBracketR, // 0x1B
&InputDeviceKeyboard::Key::EditEnter, // 0x1C
&InputDeviceKeyboard::Key::ModifierCtrlL, // 0x1D
&InputDeviceKeyboard::Key::AlphanumericA, // 0x1E
&InputDeviceKeyboard::Key::AlphanumericS, // 0x1F
&InputDeviceKeyboard::Key::AlphanumericD, // 0x20
&InputDeviceKeyboard::Key::AlphanumericF, // 0x21
&InputDeviceKeyboard::Key::AlphanumericG, // 0x22
&InputDeviceKeyboard::Key::AlphanumericH, // 0x23
&InputDeviceKeyboard::Key::AlphanumericJ, // 0x24
&InputDeviceKeyboard::Key::AlphanumericK, // 0x25
&InputDeviceKeyboard::Key::AlphanumericL, // 0x26
&InputDeviceKeyboard::Key::PunctuationSemicolon, // 0x27
&InputDeviceKeyboard::Key::PunctuationApostrophe, // 0x28
&InputDeviceKeyboard::Key::PunctuationTilde, // 0x29
&InputDeviceKeyboard::Key::ModifierShiftL, // 0x2A
&InputDeviceKeyboard::Key::PunctuationBackslash, // 0x2B
&InputDeviceKeyboard::Key::AlphanumericZ, // 0x2C
&InputDeviceKeyboard::Key::AlphanumericX, // 0x2D
&InputDeviceKeyboard::Key::AlphanumericC, // 0x2E
&InputDeviceKeyboard::Key::AlphanumericV, // 0x2F
&InputDeviceKeyboard::Key::AlphanumericB, // 0x30
&InputDeviceKeyboard::Key::AlphanumericN, // 0x31
&InputDeviceKeyboard::Key::AlphanumericM, // 0x32
&InputDeviceKeyboard::Key::PunctuationComma, // 0x33
&InputDeviceKeyboard::Key::PunctuationPeriod, // 0x34
&InputDeviceKeyboard::Key::PunctuationSlash, // 0x35
&InputDeviceKeyboard::Key::ModifierShiftR, // 0x36
&InputDeviceKeyboard::Key::NumPadMultiply, // 0x37
&InputDeviceKeyboard::Key::ModifierAltL, // 0x38
&InputDeviceKeyboard::Key::EditSpace, // 0x39
&InputDeviceKeyboard::Key::EditCapsLock, // 0x3A
&InputDeviceKeyboard::Key::Function01, // 0x3B
&InputDeviceKeyboard::Key::Function02, // 0x3C
&InputDeviceKeyboard::Key::Function03, // 0x3D
&InputDeviceKeyboard::Key::Function04, // 0x3E
&InputDeviceKeyboard::Key::Function05, // 0x3F
&InputDeviceKeyboard::Key::Function06, // 0x40
&InputDeviceKeyboard::Key::Function07, // 0x41
&InputDeviceKeyboard::Key::Function08, // 0x42
&InputDeviceKeyboard::Key::Function09, // 0x43
&InputDeviceKeyboard::Key::Function10, // 0x44
&InputDeviceKeyboard::Key::NumLock, // 0x45
&InputDeviceKeyboard::Key::WindowsSystemScrollLock, // 0x46
&InputDeviceKeyboard::Key::NumPad7, // 0x47
&InputDeviceKeyboard::Key::NumPad8, // 0x48
&InputDeviceKeyboard::Key::NumPad9, // 0x49
&InputDeviceKeyboard::Key::NumPadSubtract, // 0x4A
&InputDeviceKeyboard::Key::NumPad4, // 0x4B
&InputDeviceKeyboard::Key::NumPad5, // 0x4C
&InputDeviceKeyboard::Key::NumPad6, // 0x4D
&InputDeviceKeyboard::Key::NumPadAdd, // 0x4E
&InputDeviceKeyboard::Key::NumPad1, // 0x4F
&InputDeviceKeyboard::Key::NumPad2, // 0x50
&InputDeviceKeyboard::Key::NumPad3, // 0x51
&InputDeviceKeyboard::Key::NumPad0, // 0x52
&InputDeviceKeyboard::Key::NumPadDecimal, // 0x53
nullptr, // Sys Req? // 0x54
nullptr, // 0x55
&InputDeviceKeyboard::Key::SupplementaryISO, // 0x56
&InputDeviceKeyboard::Key::Function11, // 0x57
&InputDeviceKeyboard::Key::Function12, // 0x58
&InputDeviceKeyboard::Key::WindowsSystemPause, // 0x59
nullptr, // 0x5A
&InputDeviceKeyboard::Key::ModifierSuperL, // 0x5B
&InputDeviceKeyboard::Key::ModifierSuperR, // 0x5C
nullptr, // 0x5D
nullptr, // 0x5E
nullptr, // 0x5F
nullptr, // 0x60
nullptr, // 0x61
nullptr, // 0x62
nullptr, // 0x63
&InputDeviceKeyboard::Key::Function13, // 0x64
&InputDeviceKeyboard::Key::Function14, // 0x65
&InputDeviceKeyboard::Key::Function15, // 0x66
&InputDeviceKeyboard::Key::Function16, // 0x67
&InputDeviceKeyboard::Key::Function17, // 0x68
&InputDeviceKeyboard::Key::Function18, // 0x69
&InputDeviceKeyboard::Key::Function19, // 0x6A
nullptr, // 0x6B
nullptr, // 0x6C
nullptr, // 0x6D
nullptr, // 0x6E
nullptr, // 0x6F
nullptr, // 0x70
nullptr, // 0x71
nullptr, // 0x72
nullptr, // 0x73
nullptr, // 0x74
nullptr, // 0x75
nullptr, // 0x76
nullptr, // 0x77
nullptr, // 0x78
nullptr, // 0x79
nullptr, // 0x7A
nullptr, // 0x7B
nullptr, // 0x7C
nullptr, // 0x7D
nullptr, // 0x7E
nullptr // 0x7F
}};
////////////////////////////////////////////////////////////////////////////////////////////////
// Table of key ids indexed by their windows scan code if the E0 'extended bit' prefix is set
const AZStd::array<const InputChannelId*, 128> InputChannelIdByScanCodeWithExtendedPrefixTable =
{{
nullptr, // 0x00
nullptr, // 0x01
nullptr, // 0x02
nullptr, // 0x03
nullptr, // 0x04
nullptr, // 0x05
nullptr, // 0x06
nullptr, // 0x07
nullptr, // 0x08
nullptr, // 0x09
nullptr, // 0x0A
nullptr, // 0x0B
nullptr, // 0x0C
nullptr, // 0x0D
nullptr, // 0x0E
nullptr, // 0x0F
nullptr, // 0x10
nullptr, // 0x11
nullptr, // 0x12
nullptr, // 0x13
nullptr, // 0x14
nullptr, // 0x15
nullptr, // 0x16
nullptr, // 0x17
nullptr, // 0x18
nullptr, // 0x19
nullptr, // 0x1A
nullptr, // 0x1B
&InputDeviceKeyboard::Key::NumPadEnter, // 0x1C
&InputDeviceKeyboard::Key::ModifierCtrlR, // 0x1D
nullptr, // 0x1E
nullptr, // 0x1F
nullptr, // 0x20
nullptr, // 0x21
nullptr, // 0x22
nullptr, // 0x23
nullptr, // 0x24
nullptr, // 0x25
nullptr, // 0x26
nullptr, // 0x27
nullptr, // 0x28
nullptr, // 0x29
&InputDeviceKeyboard::Key::WindowsSystemPrint, // 0x2A
nullptr, // 0x2B
nullptr, // 0x2C
nullptr, // 0x2D
nullptr, // 0x2E
nullptr, // 0x2F
nullptr, // 0x30
nullptr, // 0x31
nullptr, // 0x32
nullptr, // 0x33
nullptr, // 0x34
&InputDeviceKeyboard::Key::NumPadDivide, // 0x35
nullptr, // 0x36
&InputDeviceKeyboard::Key::WindowsSystemPrint, // 0x37
&InputDeviceKeyboard::Key::ModifierAltR, // 0x38
nullptr, // 0x39
nullptr, // 0x3A
nullptr, // 0x3B
nullptr, // 0x3C
nullptr, // 0x3D
nullptr, // 0x3E
nullptr, // 0x3F
nullptr, // 0x40
nullptr, // 0x41
nullptr, // 0x42
nullptr, // 0x43
nullptr, // 0x44
nullptr, // 0x45
nullptr, // 0x46
&InputDeviceKeyboard::Key::NavigationHome, // 0x47
&InputDeviceKeyboard::Key::NavigationArrowUp, // 0x48
&InputDeviceKeyboard::Key::NavigationPageUp, // 0x49
nullptr, // 0x4A
&InputDeviceKeyboard::Key::NavigationArrowLeft, // 0x4B
nullptr, // 0x4C
&InputDeviceKeyboard::Key::NavigationArrowRight, // 0x4D
nullptr, // 0x4E
&InputDeviceKeyboard::Key::NavigationEnd, // 0x4F
&InputDeviceKeyboard::Key::NavigationArrowDown, // 0x50
&InputDeviceKeyboard::Key::NavigationPageDown, // 0x51
&InputDeviceKeyboard::Key::NavigationInsert, // 0x52
&InputDeviceKeyboard::Key::NavigationDelete, // 0x53
nullptr, // 0x54
nullptr, // 0x55
nullptr, // 0x56
nullptr, // 0x57
nullptr, // 0x58
nullptr, // 0x59
nullptr, // 0x5A
nullptr, // 0x5B
nullptr, // 0x5C
nullptr, // 0x5D
nullptr, // 0x5E
nullptr, // 0x5F
nullptr, // 0x60
nullptr, // 0x61
nullptr, // 0x62
nullptr, // 0x63
nullptr, // 0x64
nullptr, // 0x65
nullptr, // 0x66
nullptr, // 0x67
nullptr, // 0x68
nullptr, // 0x69
nullptr, // 0x6A
nullptr, // 0x6B
nullptr, // 0x6C
nullptr, // 0x6D
nullptr, // 0x6E
nullptr, // 0x6F
nullptr, // 0x70
nullptr, // 0x71
nullptr, // 0x72
nullptr, // 0x73
nullptr, // 0x74
nullptr, // 0x75
nullptr, // 0x76
nullptr, // 0x77
nullptr, // 0x78
nullptr, // 0x79
nullptr, // 0x7A
nullptr, // 0x7B
nullptr, // 0x7C
nullptr, // 0x7D
nullptr, // 0x7E
nullptr // 0x7F
}};
////////////////////////////////////////////////////////////////////////////////////////////////
// Table of key ids indexed by their virtual key code. This should only be used as a last resort
// if the input channel id cannot be determined directly from the scan code, as some scan codes
// generate the same virtual key code (eg. the 'enter' and 'numpad enter' keys), or can produce
// different virtual key codes depending on the state of other keys (eg. some of the number pad
// keys produce different virtual key codes depending if the numlock key is toggled on or off).
//
// One exception is the system pause key, which generates the same scan code as the numlock key,
// meaning we must check the virtual key code to be able to distinguish between these two keys.
const AZStd::array<const InputChannelId*, 255> InputChannelIdByVirtualKeyCodeTable =
{{
nullptr, // 0x00
nullptr, // 0x01 VK_LBUTTON
nullptr, // 0x02 VK_RBUTTON
nullptr, // 0x03 VK_CANCEL
nullptr, // 0x04 VK_MBUTTON
nullptr, // 0x05 VK_XBUTTON1
nullptr, // 0x06 VK_XBUTTON2
nullptr, // 0x07
&InputDeviceKeyboard::Key::EditBackspace, // 0x08 VK_BACK
&InputDeviceKeyboard::Key::EditTab, // 0x09 VK_TAB
nullptr, // 0x0A
nullptr, // 0x0B
nullptr, // 0x0C VK_CLEAR
&InputDeviceKeyboard::Key::EditEnter, // 0x0D VK_RETURN
nullptr, // 0x0E
nullptr, // 0x0F
nullptr, // 0x10 VK_SHIFT
nullptr, // 0x11 VK_CONTROL
nullptr, // 0x12 VK_MENU
&InputDeviceKeyboard::Key::WindowsSystemPause, // 0x13 VK_PAUSE
&InputDeviceKeyboard::Key::EditCapsLock, // 0x14 VK_CAPITAL
nullptr, // 0x15 VK_KANA
nullptr, // 0x16
nullptr, // 0x17 VK_JUNJA
nullptr, // 0x18 VK_FINAL
nullptr, // 0x19 VK_KANJI
nullptr, // 0x1A
&InputDeviceKeyboard::Key::Escape, // 0x1B VK_ESCAPE
nullptr, // 0x1C VK_CONVERT
nullptr, // 0x1D VK_NONCONVERT
nullptr, // 0x1E VK_ACCEPT
nullptr, // 0x1F VK_MODECHANGE
&InputDeviceKeyboard::Key::EditSpace, // 0x20 VK_SPACE
&InputDeviceKeyboard::Key::NavigationPageUp, // 0x21 VK_PRIOR
&InputDeviceKeyboard::Key::NavigationPageDown, // 0x22 VK_NEXT
&InputDeviceKeyboard::Key::NavigationEnd, // 0x23 VK_END
&InputDeviceKeyboard::Key::NavigationHome, // 0x24 VK_HOME
&InputDeviceKeyboard::Key::NavigationArrowLeft, // 0x25 VK_LEFT
&InputDeviceKeyboard::Key::NavigationArrowUp, // 0x26 VK_UP
&InputDeviceKeyboard::Key::NavigationArrowRight, // 0x27 VK_RIGHT
&InputDeviceKeyboard::Key::NavigationArrowDown, // 0x28 VK_DOWN
nullptr, // 0x29 VK_SELECT
nullptr, // 0x2A VK_PRINT
nullptr, // 0x2B VK_EXECUTE
&InputDeviceKeyboard::Key::WindowsSystemPrint, // 0x2C VK_SNAPSHOT
&InputDeviceKeyboard::Key::NavigationInsert, // 0x2D VK_INSERT
&InputDeviceKeyboard::Key::NavigationDelete, // 0x2E VK_DELETE
nullptr, // 0x2F VK_HELP
&InputDeviceKeyboard::Key::Alphanumeric0, // 0x30
&InputDeviceKeyboard::Key::Alphanumeric1, // 0x31
&InputDeviceKeyboard::Key::Alphanumeric2, // 0x32
&InputDeviceKeyboard::Key::Alphanumeric3, // 0x33
&InputDeviceKeyboard::Key::Alphanumeric4, // 0x34
&InputDeviceKeyboard::Key::Alphanumeric5, // 0x35
&InputDeviceKeyboard::Key::Alphanumeric6, // 0x36
&InputDeviceKeyboard::Key::Alphanumeric7, // 0x37
&InputDeviceKeyboard::Key::Alphanumeric8, // 0x38
&InputDeviceKeyboard::Key::Alphanumeric9, // 0x39
nullptr, // 0x3A
nullptr, // 0x3B
nullptr, // 0x3C
nullptr, // 0x3D
nullptr, // 0x3E
nullptr, // 0x3F
nullptr, // 0x40
&InputDeviceKeyboard::Key::AlphanumericA, // 0x41
&InputDeviceKeyboard::Key::AlphanumericB, // 0x42
&InputDeviceKeyboard::Key::AlphanumericC, // 0x43
&InputDeviceKeyboard::Key::AlphanumericD, // 0x44
&InputDeviceKeyboard::Key::AlphanumericE, // 0x45
&InputDeviceKeyboard::Key::AlphanumericF, // 0x46
&InputDeviceKeyboard::Key::AlphanumericG, // 0x47
&InputDeviceKeyboard::Key::AlphanumericH, // 0x48
&InputDeviceKeyboard::Key::AlphanumericI, // 0x49
&InputDeviceKeyboard::Key::AlphanumericJ, // 0x4A
&InputDeviceKeyboard::Key::AlphanumericK, // 0x4B
&InputDeviceKeyboard::Key::AlphanumericL, // 0x4C
&InputDeviceKeyboard::Key::AlphanumericM, // 0x4D
&InputDeviceKeyboard::Key::AlphanumericN, // 0x4E
&InputDeviceKeyboard::Key::AlphanumericO, // 0x4F
&InputDeviceKeyboard::Key::AlphanumericP, // 0x50
&InputDeviceKeyboard::Key::AlphanumericQ, // 0x51
&InputDeviceKeyboard::Key::AlphanumericR, // 0x52
&InputDeviceKeyboard::Key::AlphanumericS, // 0x53
&InputDeviceKeyboard::Key::AlphanumericT, // 0x54
&InputDeviceKeyboard::Key::AlphanumericU, // 0x55
&InputDeviceKeyboard::Key::AlphanumericV, // 0x56
&InputDeviceKeyboard::Key::AlphanumericW, // 0x57
&InputDeviceKeyboard::Key::AlphanumericX, // 0x58
&InputDeviceKeyboard::Key::AlphanumericY, // 0x59
&InputDeviceKeyboard::Key::AlphanumericZ, // 0x5A
&InputDeviceKeyboard::Key::ModifierSuperL, // 0x5B VK_LWIN
&InputDeviceKeyboard::Key::ModifierSuperR, // 0x5C VK_RWIN
nullptr, // 0x5D VK_APPS
nullptr, // 0x5E
nullptr, // 0x5F VK_SLEEP
&InputDeviceKeyboard::Key::NumPad0, // 0x60 VK_NUMPAD0
&InputDeviceKeyboard::Key::NumPad1, // 0x61 VK_NUMPAD1
&InputDeviceKeyboard::Key::NumPad2, // 0x62 VK_NUMPAD2
&InputDeviceKeyboard::Key::NumPad3, // 0x63 VK_NUMPAD3
&InputDeviceKeyboard::Key::NumPad4, // 0x64 VK_NUMPAD4
&InputDeviceKeyboard::Key::NumPad5, // 0x65 VK_NUMPAD5
&InputDeviceKeyboard::Key::NumPad6, // 0x66 VK_NUMPAD6
&InputDeviceKeyboard::Key::NumPad7, // 0x67 VK_NUMPAD7
&InputDeviceKeyboard::Key::NumPad8, // 0x68 VK_NUMPAD8
&InputDeviceKeyboard::Key::NumPad9, // 0x69 VK_NUMPAD9
&InputDeviceKeyboard::Key::NumPadMultiply, // 0x6A VK_MULTIPLY
&InputDeviceKeyboard::Key::NumPadAdd, // 0x6B VK_ADD
&InputDeviceKeyboard::Key::NumPadEnter, // 0x6C VK_SEPARATOR
&InputDeviceKeyboard::Key::NumPadSubtract, // 0x6D VK_SUBTRACT
&InputDeviceKeyboard::Key::NumPadDecimal, // 0x6E VK_DECIMAL
&InputDeviceKeyboard::Key::NumPadDivide, // 0x6F VK_DIVIDE
&InputDeviceKeyboard::Key::Function01, // 0x70 VK_F1
&InputDeviceKeyboard::Key::Function02, // 0x71 VK_F2
&InputDeviceKeyboard::Key::Function03, // 0x72 VK_F3
&InputDeviceKeyboard::Key::Function04, // 0x73 VK_F4
&InputDeviceKeyboard::Key::Function05, // 0x74 VK_F5
&InputDeviceKeyboard::Key::Function06, // 0x75 VK_F6
&InputDeviceKeyboard::Key::Function07, // 0x76 VK_F7
&InputDeviceKeyboard::Key::Function08, // 0x77 VK_F8
&InputDeviceKeyboard::Key::Function09, // 0x78 VK_F9
&InputDeviceKeyboard::Key::Function10, // 0x79 VK_F10
&InputDeviceKeyboard::Key::Function11, // 0x7A VK_F11
&InputDeviceKeyboard::Key::Function12, // 0x7B VK_F12
&InputDeviceKeyboard::Key::Function13, // 0x7C VK_F13
&InputDeviceKeyboard::Key::Function14, // 0x7D VK_F14
&InputDeviceKeyboard::Key::Function15, // 0x7E VK_F15
&InputDeviceKeyboard::Key::Function16, // 0x7F VK_F16
&InputDeviceKeyboard::Key::Function17, // 0x80 VK_F17
&InputDeviceKeyboard::Key::Function18, // 0x81 VK_F18
&InputDeviceKeyboard::Key::Function19, // 0x82 VK_F19
&InputDeviceKeyboard::Key::Function20, // 0x83 VK_F20
nullptr, // 0x84 VK_F21
nullptr, // 0x85 VK_F22
nullptr, // 0x86 VK_F23
nullptr, // 0x87 VK_F24
nullptr, // 0x88
nullptr, // 0x89
nullptr, // 0x8A
nullptr, // 0x8B
nullptr, // 0x8C
nullptr, // 0x8D
nullptr, // 0x8E
nullptr, // 0x8F
&InputDeviceKeyboard::Key::NumLock, // 0x90 VK_NUMLOCK
&InputDeviceKeyboard::Key::WindowsSystemScrollLock, // 0x91 VK_SCROLL
nullptr, // 0x92
nullptr, // 0x93
nullptr, // 0x94
nullptr, // 0x95
nullptr, // 0x96
nullptr, // 0x97
nullptr, // 0x98
nullptr, // 0x99
nullptr, // 0x9A
nullptr, // 0x9B
nullptr, // 0x9C
nullptr, // 0x9D
nullptr, // 0x9E
nullptr, // 0x9F
&InputDeviceKeyboard::Key::ModifierShiftL, // 0xA0 VK_LSHIFT
&InputDeviceKeyboard::Key::ModifierShiftR, // 0xA1 VK_RSHIFT
&InputDeviceKeyboard::Key::ModifierCtrlL, // 0xA2 VK_LCONTROL
&InputDeviceKeyboard::Key::ModifierCtrlR, // 0xA3 VK_RCONTROL
&InputDeviceKeyboard::Key::ModifierAltL, // 0xA4 VK_LMENU
&InputDeviceKeyboard::Key::ModifierAltR, // 0xA5 VK_RMENU
nullptr, // 0xA6 VK_BROWSER_BACK
nullptr, // 0xA7 VK_BROWSER_FORWARD
nullptr, // 0xA8 VK_BROWSER_REFRESH
nullptr, // 0xA9 VK_BROWSER_STOP
nullptr, // 0xAA VK_BROWSER_SEARCH
nullptr, // 0xAB VK_BROWSER_FAVORITES
nullptr, // 0xAC VK_BROWSER_HOME
nullptr, // 0xAD VK_VOLUME_MUTE
nullptr, // 0xAE VK_VOLUME_DOWN
nullptr, // 0xAF VK_VOLUME_UP
nullptr, // 0xB0 VK_MEDIA_NEXT_TRACK
nullptr, // 0xB1 VK_MEDIA_PREV_TRACK
nullptr, // 0xB2 VK_MEDIA_STOP
nullptr, // 0xB3 VK_MEDIA_PLAY_PAUSE
nullptr, // 0xB4 VK_LAUNCH_MAIL
nullptr, // 0xB5 VK_LAUNCH_MEDIA_SELECT
nullptr, // 0xB6 VK_LAUNCH_APP1
nullptr, // 0xB7 VK_LAUNCH_APP2
nullptr, // 0xB8
nullptr, // 0xB9
&InputDeviceKeyboard::Key::PunctuationSemicolon, // 0xBA VK_OEM_1
&InputDeviceKeyboard::Key::PunctuationEquals, // 0xBB VK_OEM_PLUS
&InputDeviceKeyboard::Key::PunctuationComma, // 0xBC VK_OEM_COMMA
&InputDeviceKeyboard::Key::PunctuationHyphen, // 0xBD VK_OEM_MINUS
&InputDeviceKeyboard::Key::PunctuationPeriod, // 0xBE VK_OEM_PERIOD
&InputDeviceKeyboard::Key::PunctuationSlash, // 0xBF VK_OEM_2
&InputDeviceKeyboard::Key::PunctuationTilde, // 0xC0 VK_OEM_3
nullptr, // 0xC1
nullptr, // 0xC2
nullptr, // 0xC3
nullptr, // 0xC4
nullptr, // 0xC5
nullptr, // 0xC6
nullptr, // 0xC7
nullptr, // 0xC8
nullptr, // 0xC9
nullptr, // 0xCA
nullptr, // 0xCB
nullptr, // 0xCC
nullptr, // 0xCD
nullptr, // 0xCE
nullptr, // 0xCF
nullptr, // 0xD0
nullptr, // 0xD1
nullptr, // 0xD2
nullptr, // 0xD3
nullptr, // 0xD4
nullptr, // 0xD5
nullptr, // 0xD6
nullptr, // 0xD7
nullptr, // 0xD8
nullptr, // 0xD9
nullptr, // 0xDA
&InputDeviceKeyboard::Key::PunctuationBracketL, // 0xDB VK_OEM_4
&InputDeviceKeyboard::Key::PunctuationBackslash, // 0xDC VK_OEM_5
&InputDeviceKeyboard::Key::PunctuationBracketR, // 0xDD VK_OEM_6
&InputDeviceKeyboard::Key::PunctuationApostrophe, // 0xDE VK_OEM_7
nullptr, // 0xDF VK_OEM_8
nullptr, // 0xE0
nullptr, // 0xE1
&InputDeviceKeyboard::Key::SupplementaryISO, // 0xE2 VK_OEM_102
nullptr, // 0xE3
nullptr, // 0xE4
nullptr, // 0xE5 VK_PROCESSKEY
nullptr, // 0xE6
nullptr, // 0xE7 VK_PACKET
nullptr, // 0xE8
nullptr, // 0xE9
nullptr, // 0xEA
nullptr, // 0xEB
nullptr, // 0xEC
nullptr, // 0xED
nullptr, // 0xEE
nullptr, // 0xEF
nullptr, // 0xF0
nullptr, // 0xF1
nullptr, // 0xF2
nullptr, // 0xF3
nullptr, // 0xF4
nullptr, // 0xF5
nullptr, // 0xF6 VK_ATTN
nullptr, // 0xF7 VK_CRSEL
nullptr, // 0xF8 VK_EXSEL
nullptr, // 0xF9 VK_EREOF
nullptr, // 0xFA VK_PLAY
nullptr, // 0xFB VK_ZOOM
nullptr, // 0xFC VK_NONAME
nullptr, // 0xFD VK_PA1
nullptr // 0xFE VK_OEM_CLEAR
}};
}
@@ -0,0 +1,339 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Devices/Motion/InputDeviceMotion.h>
#include <AzCore/Debug/Trace.h>
#include <AzCore/RTTI/BehaviorContext.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
const InputDeviceId InputDeviceMotion::Id("motion");
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceMotion::IsMotionDevice(const InputDeviceId& inputDeviceId)
{
return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32());
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceMotion::Acceleration::Gravity("motion_acceleration_gravity");
const InputChannelId InputDeviceMotion::Acceleration::Raw("motion_acceleration_raw");
const InputChannelId InputDeviceMotion::Acceleration::User("motion_acceleration_user");
const AZStd::array<InputChannelId, 3> InputDeviceMotion::Acceleration::All =
{{
Gravity,
Raw,
User
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceMotion::RotationRate::Raw("motion_rotation_rate_raw");
const InputChannelId InputDeviceMotion::RotationRate::Unbiased("motion_rotation_rate_unbiased");
const AZStd::array<InputChannelId, 2> InputDeviceMotion::RotationRate::All =
{{
Raw,
Unbiased
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceMotion::MagneticField::North("motion_magnetic_field_north");
const InputChannelId InputDeviceMotion::MagneticField::Raw("motion_magnetic_field_raw");
const InputChannelId InputDeviceMotion::MagneticField::Unbiased("motion_magnetic_field_unbiased");
const AZStd::array<InputChannelId, 3> InputDeviceMotion::MagneticField::All =
{{
North,
Raw,
Unbiased
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceMotion::Orientation::Current("motion_orientation_current");
const AZStd::array<InputChannelId, 1> InputDeviceMotion::Orientation::All =
{{
Current
}};
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMotion::Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
// Unfortunately it doesn't seem possible to reflect anything through BehaviorContext
// using lambdas which capture variables from the enclosing scope. So we are manually
// reflecting all input channel names, instead of just iterating over them like this:
//
// auto classBuilder = behaviorContext->Class<InputDeviceMotion>();
// for (const InputChannelId& channelId : Acceleration::All)
// {
// const char* channelName = channelId.GetName();
// classBuilder->Constant(channelName, [channelName]() { return channelName; });
// }
behaviorContext->Class<InputDeviceMotion>()
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::RuntimeOwn)
->Constant("name", BehaviorConstant(Id.GetName()))
->Constant(Acceleration::Gravity.GetName(), BehaviorConstant(Acceleration::Gravity.GetName()))
->Constant(Acceleration::Raw.GetName(), BehaviorConstant(Acceleration::Raw.GetName()))
->Constant(Acceleration::User.GetName(), BehaviorConstant(Acceleration::User.GetName()))
->Constant(RotationRate::Raw.GetName(), BehaviorConstant(RotationRate::Raw.GetName()))
->Constant(RotationRate::Unbiased.GetName(), BehaviorConstant(RotationRate::Unbiased.GetName()))
->Constant(MagneticField::North.GetName(), BehaviorConstant(MagneticField::North.GetName()))
->Constant(MagneticField::Raw.GetName(), BehaviorConstant(MagneticField::Raw.GetName()))
->Constant(MagneticField::Unbiased.GetName(), BehaviorConstant(MagneticField::Unbiased.GetName()))
->Constant(Orientation::Current.GetName(), BehaviorConstant(Orientation::Current.GetName()))
;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceMotion::InputDeviceMotion()
: InputDevice(Id)
, m_allChannelsById()
, m_accelerationChannelsById()
, m_rotationRateChannelsById()
, m_magneticFieldChannelsById()
, m_orientationChannelsById()
, m_enabledMotionChannelIds()
, m_pimpl()
, m_implementationRequestHandler(*this)
{
// Create all acceleration input channels
for (AZ::u32 i = 0; i < Acceleration::All.size(); ++i)
{
const InputChannelId& channelId = Acceleration::All[i];
InputChannelAxis3D* channel = aznew InputChannelAxis3D(channelId, *this);
m_allChannelsById[channelId] = channel;
m_accelerationChannelsById[channelId] = channel;
}
// Create all rotation rate input channels
for (AZ::u32 i = 0; i < RotationRate::All.size(); ++i)
{
const InputChannelId& channelId = RotationRate::All[i];
InputChannelAxis3D* channel = aznew InputChannelAxis3D(channelId, *this);
m_allChannelsById[channelId] = channel;
m_rotationRateChannelsById[channelId] = channel;
}
// Create all magnetic field input channels
for (AZ::u32 i = 0; i < MagneticField::All.size(); ++i)
{
const InputChannelId& channelId = MagneticField::All[i];
InputChannelAxis3D* channel = aznew InputChannelAxis3D(channelId, *this);
m_allChannelsById[channelId] = channel;
m_magneticFieldChannelsById[channelId] = channel;
}
// Create all orientation input channels
for (AZ::u32 i = 0; i < Orientation::All.size(); ++i)
{
const InputChannelId& channelId = Orientation::All[i];
InputChannelQuaternion* channel = aznew InputChannelQuaternion(channelId, *this);
m_allChannelsById[channelId] = channel;
m_orientationChannelsById[channelId] = channel;
}
// Create the platform specific implementation
m_pimpl.reset(Implementation::Create(*this));
// Connect to the motion sensor request bus
InputMotionSensorRequestBus::Handler::BusConnect(GetInputDeviceId());
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceMotion::~InputDeviceMotion()
{
// Disconnect from the motion sensor request bus
InputMotionSensorRequestBus::Handler::BusDisconnect(GetInputDeviceId());
// Destroy the platform specific implementation
m_pimpl.reset();
// Destroy all orientation input channels
for (const auto& channelById : m_orientationChannelsById)
{
delete channelById.second;
}
// Destroy all magnetic field input channels
for (const auto& channelById : m_magneticFieldChannelsById)
{
delete channelById.second;
}
// Destroy all rotation rate input channels
for (const auto& channelById : m_rotationRateChannelsById)
{
delete channelById.second;
}
// Destroy all acceleration input channels
for (const auto& channelById : m_accelerationChannelsById)
{
delete channelById.second;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputDevice::InputChannelByIdMap& InputDeviceMotion::GetInputChannelsById() const
{
return m_allChannelsById;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceMotion::IsSupported() const
{
return m_pimpl != nullptr;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceMotion::IsConnected() const
{
return m_pimpl ? m_pimpl->IsConnected() : false;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMotion::TickInputDevice()
{
if (m_pimpl)
{
m_pimpl->TickInputDevice();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMotion::SetInputChannelEnabled(const InputChannelId& channelId, bool enabled)
{
if (!m_pimpl)
{
return;
}
if (m_allChannelsById.find(channelId) == m_allChannelsById.end())
{
// The channel id is not recognized by this device
return;
}
const bool isEnabled = m_enabledMotionChannelIds.find(channelId) != m_enabledMotionChannelIds.end();
if (enabled == isEnabled)
{
// The channel is already enabled or disabled as requested
return;
}
if (enabled)
{
m_enabledMotionChannelIds.insert(channelId);
}
else
{
m_enabledMotionChannelIds.erase(channelId);
}
m_pimpl->RefreshMotionSensors(m_enabledMotionChannelIds);
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceMotion::GetInputChannelEnabled(const InputChannelId& channelId)
{
if (m_allChannelsById.find(channelId) == m_allChannelsById.end())
{
// The channel id is not recognized by this device
return false;
}
return m_enabledMotionChannelIds.find(channelId) != m_enabledMotionChannelIds.end();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMotion::OnApplicationSuspended(Event /*lastEvent*/)
{
if (m_pimpl)
{
m_pimpl->RefreshMotionSensors(InputChannelIdSet());
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMotion::OnApplicationResumed(Event /*lastEvent*/)
{
if (m_pimpl)
{
m_pimpl->RefreshMotionSensors(m_enabledMotionChannelIds);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceMotion::Implementation::Implementation(InputDeviceMotion& inputDevice)
: m_inputDevice(inputDevice)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceMotion::Implementation::~Implementation()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMotion::Implementation::ProcessAccelerationData(const InputChannelId& channelId,
const AZ::Vector3& data)
{
if (m_inputDevice.m_enabledMotionChannelIds.find(channelId) != m_inputDevice.m_enabledMotionChannelIds.end())
{
m_inputDevice.m_accelerationChannelsById[channelId]->ProcessRawInputEvent(data);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMotion::Implementation::ProcessRotationRateData(const InputChannelId& channelId,
const AZ::Vector3& data)
{
if (m_inputDevice.m_enabledMotionChannelIds.find(channelId) != m_inputDevice.m_enabledMotionChannelIds.end())
{
m_inputDevice.m_rotationRateChannelsById[channelId]->ProcessRawInputEvent(data);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMotion::Implementation::ProcessMagneticFieldData(const InputChannelId& channelId,
const AZ::Vector3& data)
{
if (m_inputDevice.m_enabledMotionChannelIds.find(channelId) != m_inputDevice.m_enabledMotionChannelIds.end())
{
m_inputDevice.m_magneticFieldChannelsById[channelId]->ProcessRawInputEvent(data);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMotion::Implementation::ProcessOrientationData(const InputChannelId& channelId,
const AZ::Quaternion& data)
{
if (m_inputDevice.m_enabledMotionChannelIds.find(channelId) != m_inputDevice.m_enabledMotionChannelIds.end())
{
m_inputDevice.m_orientationChannelsById[channelId]->ProcessRawInputEvent(data);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMotion::Implementation::ResetInputChannelStates()
{
m_inputDevice.ResetInputChannelStates();
}
} // namespace AzFramework
@@ -0,0 +1,274 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Buses/Requests/InputMotionSensorRequestBus.h>
#include <AzFramework/Input/Channels/InputChannelAxis3D.h>
#include <AzFramework/Input/Channels/InputChannelQuaternion.h>
#include <AzFramework/Input/Devices/InputDevice.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Defines a generic motion input device including the ids of all its associated input channels.
//! Platform specific implementations are defined as private implementations so that creating an
//! instance of this generic class will work correctly on any platform that support motion input,
//! while providing access to the device name and associated channel ids on any platform through
//! the 'null' implementation (primarily so that the editor can use them to setup input mappings).
class InputDeviceMotion : public InputDevice
, public InputMotionSensorRequestBus::Handler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! The id used to identify the primary motion input device
static const InputDeviceId Id;
////////////////////////////////////////////////////////////////////////////////////////////
//! Check whether an input device id identifies a motion device (regardless of index)
//! \param[in] inputDeviceId The input device id to check
//! \return True if the input device id identifies a motion device, false otherwise
static bool IsMotionDevice(const InputDeviceId& inputDeviceId);
////////////////////////////////////////////////////////////////////////////////////////////
//! All the input channel ids that identify different types of acceleration data. Note that
//! not all motion devices will necessarily be able to emit all types of motion sensor data,
//! and unlike most other input channels these ones must be explicitly enabled using either:
//! - InputSystemComponent::m_initiallyActiveMotionChannels
//! - InputMotionSensorRequests::SetInputChannelEnabled
struct Acceleration
{
static const InputChannelId Gravity;
static const InputChannelId Raw;
static const InputChannelId User;
//!< All acceleration input channel ids
static const AZStd::array<InputChannelId, 3> All;
};
////////////////////////////////////////////////////////////////////////////////////////////
//! All the input channel ids that identify different types of rotation rate data. Note that
//! not all motion devices will necessarily be able to emit all types of motion sensor data,
//! and unlike most other input channels these ones must be explicitly enabled using either:
//! - InputSystemComponent::m_initiallyActiveMotionChannels
//! - InputMotionSensorRequests::SetInputChannelEnabled
struct RotationRate
{
static const InputChannelId Raw;
static const InputChannelId Unbiased;
//!< All rotation rate input channel ids
static const AZStd::array<InputChannelId, 2> All;
};
////////////////////////////////////////////////////////////////////////////////////////////
//! All the input channel ids that identify different types of magnetic field data. Note that
//! not all motion devices will necessarily be able to emit all types of motion sensor data,
//! and unlike most other input channels these ones must be explicitly enabled using either:
//! - InputSystemComponent::m_initiallyActiveMotionChannels
//! - InputMotionSensorRequests::SetInputChannelEnabled
struct MagneticField
{
static const InputChannelId North;
static const InputChannelId Raw;
static const InputChannelId Unbiased;
//!< All magnetic field input channel ids
static const AZStd::array<InputChannelId, 3> All;
};
////////////////////////////////////////////////////////////////////////////////////////////
//! All the input channel ids that identify different types of orientation data. Note that
//! not all motion devices will necessarily be able to emit all types of motion sensor data,
//! and unlike most other input channels these ones must be explicitly enabled using either:
//! - InputSystemComponent::m_initiallyActiveMotionChannels
//! - InputMotionSensorRequests::SetInputChannelEnabled
struct Orientation
{
static const InputChannelId Current;
//!< All orientation input channel ids
static const AZStd::array<InputChannelId, 1> All;
};
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputDeviceMotion, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputDeviceMotion, "{AB8AC810-1B66-4BDA-B1D1-67DD70043650}", InputDevice);
////////////////////////////////////////////////////////////////////////////////////////////
// Reflection
static void Reflect(AZ::ReflectContext* context);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
InputDeviceMotion();
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputDeviceMotion);
////////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
~InputDeviceMotion() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::GetInputChannelsById
const InputChannelByIdMap& GetInputChannelsById() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::IsSupported
bool IsSupported() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::IsConnected
bool IsConnected() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDeviceRequests::TickInputDevice
void TickInputDevice() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputMotionSensorRequests::GetInputChannelEnabled
bool GetInputChannelEnabled(const InputChannelId& channelId) override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputMotionSensorRequests::SetInputChannelEnabled
void SetInputChannelEnabled(const InputChannelId& channelId, bool enabled) override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::ApplicationLifecycleEvents::OnApplicationSuspended
void OnApplicationSuspended(Event lastEvent) override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::ApplicationLifecycleEvents::OnApplicationSuspended
void OnApplicationResumed(Event lastEvent) override;
protected:
////////////////////////////////////////////////////////////////////////////////////////////
///@{
//! Alias for verbose container class
using AccelerationChannelByIdMap = AZStd::unordered_map<InputChannelId, InputChannelAxis3D*>;
using RotationRateChannelByIdMap = AZStd::unordered_map<InputChannelId, InputChannelAxis3D*>;
using MagneticFieldChannelByIdMap = AZStd::unordered_map<InputChannelId, InputChannelAxis3D*>;
using OrientationChannelByIdMap = AZStd::unordered_map<InputChannelId, InputChannelQuaternion*>;
///@}
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
InputChannelByIdMap m_allChannelsById; //!< All motion channels by id
AccelerationChannelByIdMap m_accelerationChannelsById; //!< Acceleration channels by id
RotationRateChannelByIdMap m_rotationRateChannelsById; //!< Rotation rate channels by id
MagneticFieldChannelByIdMap m_magneticFieldChannelsById; //!< Magnetic field channels by id
OrientationChannelByIdMap m_orientationChannelsById; //!< Orientation channels by id
InputChannelIdSet m_enabledMotionChannelIds; //!< Currently enabled channels ids
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Base class for platform specific implementations of motion input devices
class Implementation
{
public:
////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(Implementation, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////
//! Default factory create function
//! \param[in] inputDevice Reference to the input device being implemented
static Implementation* Create(InputDeviceMotion& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputDevice Reference to the input device being implemented
Implementation(InputDeviceMotion& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(Implementation);
////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
virtual ~Implementation();
////////////////////////////////////////////////////////////////////////////////////////
//! Query the connected state of the input device
//! \return True if the input device is currently connected, false otherwise
virtual bool IsConnected() const = 0;
////////////////////////////////////////////////////////////////////////////////////////
//! Tick/update the input device to broadcast all input events since the last frame
virtual void TickInputDevice() = 0;
////////////////////////////////////////////////////////////////////////////////////////
//! Refresh the currently enabled motion sensors based on the channels that are enabled
//! \param[in] enabledChannelIds Set of motion input channel ids that should be enabled
virtual void RefreshMotionSensors(const InputChannelIdSet& enabledChannelIds) = 0;
protected:
////////////////////////////////////////////////////////////////////////////////////////
//! Process raw motion sensor data that has been obtained during since the last frame
//! This function is not thread safe, and so should only be called from the main thread.
//! \param[in] channelId The input channel id
//! \param[in] data The raw motion sensor data
void ProcessAccelerationData(const InputChannelId& channelId, const AZ::Vector3& data);
////////////////////////////////////////////////////////////////////////////////////////
//! Process raw motion sensor data that has been obtained during since the last frame
//! This function is not thread safe, and so should only be called from the main thread.
//! \param[in] channelId The input channel id
//! \param[in] data The raw motion sensor data
void ProcessRotationRateData(const InputChannelId& channelId, const AZ::Vector3& data);
////////////////////////////////////////////////////////////////////////////////////////
//! Process raw motion sensor data that has been obtained during since the last frame
//! This function is not thread safe, and so should only be called from the main thread.
//! \param[in] channelId The input channel id
//! \param[in] data The raw motion sensor data
void ProcessMagneticFieldData(const InputChannelId& channelId, const AZ::Vector3& data);
////////////////////////////////////////////////////////////////////////////////////////
//! Process raw motion sensor data that has been obtained during since the last frame
//! This function is not thread safe, and so should only be called from the main thread.
//! \param[in] channelId The input channel id
//! \param[in] data The raw motion sensor data
void ProcessOrientationData(const InputChannelId& channelId, const AZ::Quaternion& data);
////////////////////////////////////////////////////////////////////////////////////////
//! Reset the state of all this input device's associated input channels
void ResetInputChannelStates();
private:
////////////////////////////////////////////////////////////////////////////////////////
// Variables
InputDeviceMotion& m_inputDevice; //!< Reference to the input device
};
////////////////////////////////////////////////////////////////////////////////////////////
//! Set the implementation of this input device
//! \param[in] implementation The new implementation
void SetImplementation(AZStd::unique_ptr<Implementation> impl) { m_pimpl = AZStd::move(impl); }
private:
////////////////////////////////////////////////////////////////////////////////////////////
//! Private pointer to the platform specific implementation
AZStd::unique_ptr<Implementation> m_pimpl;
////////////////////////////////////////////////////////////////////////////////////////////
//! Helper class that handles requests to create a custom implementation for this device
InputDeviceImplementationRequestHandler<InputDeviceMotion> m_implementationRequestHandler;
};
} // namespace AzFramework
@@ -0,0 +1,324 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Input/Utils/ProcessRawInputEventQueues.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/std/smart_ptr/make_shared.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
const AZ::u32 InputDeviceMouse::MovementSampleRateDefault = 60;
////////////////////////////////////////////////////////////////////////////////////////////////
const AZ::u32 InputDeviceMouse::MovementSampleRateQueueAll = std::numeric_limits<AZ::u32>::max();
////////////////////////////////////////////////////////////////////////////////////////////////
const AZ::u32 InputDeviceMouse::MovementSampleRateAccumulateAll = 0;
////////////////////////////////////////////////////////////////////////////////////////////////
const InputDeviceId InputDeviceMouse::Id("mouse");
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceMouse::IsMouseDevice(const InputDeviceId& inputDeviceId)
{
return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32());
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceMouse::Button::Left("mouse_button_left");
const InputChannelId InputDeviceMouse::Button::Right("mouse_button_right");
const InputChannelId InputDeviceMouse::Button::Middle("mouse_button_middle");
const InputChannelId InputDeviceMouse::Button::Other1("mouse_button_other1");
const InputChannelId InputDeviceMouse::Button::Other2("mouse_button_other2");
const AZStd::array<InputChannelId, 5> InputDeviceMouse::Button::All =
{{
Left,
Right,
Middle,
Other1,
Other2
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceMouse::Movement::X("mouse_delta_x");
const InputChannelId InputDeviceMouse::Movement::Y("mouse_delta_y");
const InputChannelId InputDeviceMouse::Movement::Z("mouse_delta_z");
const AZStd::array<InputChannelId, 3> InputDeviceMouse::Movement::All =
{{
X,
Y,
Z
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceMouse::SystemCursorPosition("mouse_system_cursor_position");
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMouse::Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
// Unfortunately it doesn't seem possible to reflect anything through BehaviorContext
// using lambdas which capture variables from the enclosing scope. So we are manually
// reflecting all input channel names, instead of just iterating over them like this:
//
// auto classBuilder = behaviorContext->Class<InputDeviceMouse>();
// for (const InputChannelId& channelId : Button::All)
// {
// const char* channelName = channelId.GetName();
// classBuilder->Constant(channelName, [channelName]() { return channelName; });
// }
behaviorContext->Class<InputDeviceMouse>()
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::RuntimeOwn)
->Constant("name", BehaviorConstant(Id.GetName()))
->Constant(Button::Left.GetName(), BehaviorConstant(Button::Left.GetName()))
->Constant(Button::Right.GetName(), BehaviorConstant(Button::Right.GetName()))
->Constant(Button::Middle.GetName(), BehaviorConstant(Button::Middle.GetName()))
->Constant(Button::Other1.GetName(), BehaviorConstant(Button::Other1.GetName()))
->Constant(Button::Other2.GetName(), BehaviorConstant(Button::Other2.GetName()))
->Constant(Movement::X.GetName(), BehaviorConstant(Movement::X.GetName()))
->Constant(Movement::Y.GetName(), BehaviorConstant(Movement::Y.GetName()))
->Constant(Movement::Z.GetName(), BehaviorConstant(Movement::Z.GetName()))
;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceMouse::InputDeviceMouse()
: InputDevice(Id)
, m_allChannelsById()
, m_buttonChannelsById()
, m_movementChannelsById()
, m_cursorPositionChannel()
, m_cursorPositionData2D(AZStd::make_shared<InputChannel::PositionData2D>())
, m_pimpl()
, m_implementationRequestHandler(*this)
{
// Create all button input channels
for (const InputChannelId& channelId : Button::All)
{
InputChannelDigitalWithSharedPosition2D* channel = aznew InputChannelDigitalWithSharedPosition2D(channelId, *this, m_cursorPositionData2D);
m_allChannelsById[channelId] = channel;
m_buttonChannelsById[channelId] = channel;
}
// Create all movement input channels
for (const InputChannelId& channelId : Movement::All)
{
InputChannelDeltaWithSharedPosition2D* channel = aznew InputChannelDeltaWithSharedPosition2D(channelId, *this, m_cursorPositionData2D);
m_allChannelsById[channelId] = channel;
m_movementChannelsById[channelId] = channel;
}
// Create the cursor position input channel
m_cursorPositionChannel = aznew InputChannelDeltaWithSharedPosition2D(SystemCursorPosition, *this, m_cursorPositionData2D);
m_allChannelsById[SystemCursorPosition] = m_cursorPositionChannel;
// Create the platform specific implementation
m_pimpl.reset(Implementation::Create(*this));
// Connect to the system cursor request bus
InputSystemCursorRequestBus::Handler::BusConnect(GetInputDeviceId());
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceMouse::~InputDeviceMouse()
{
// Disconnect from the system cursor request bus
InputSystemCursorRequestBus::Handler::BusDisconnect(GetInputDeviceId());
// Destroy the platform specific implementation
m_pimpl.reset();
// Destroy the cursor position input channel
delete m_cursorPositionChannel;
// Destroy all movement input channels
for (const auto& channelById : m_movementChannelsById)
{
delete channelById.second;
}
// Destroy all button input channels
for (const auto& channelById : m_buttonChannelsById)
{
delete channelById.second;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputDevice::InputChannelByIdMap& InputDeviceMouse::GetInputChannelsById() const
{
return m_allChannelsById;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceMouse::IsSupported() const
{
return m_pimpl != nullptr;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceMouse::IsConnected() const
{
return m_pimpl ? m_pimpl->IsConnected() : false;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMouse::TickInputDevice()
{
if (m_pimpl)
{
m_pimpl->TickInputDevice();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMouse::SetSystemCursorState(SystemCursorState systemCursorState)
{
if (m_pimpl)
{
m_pimpl->SetSystemCursorState(systemCursorState);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
SystemCursorState InputDeviceMouse::GetSystemCursorState() const
{
return m_pimpl ? m_pimpl->GetSystemCursorState() : SystemCursorState::Unknown;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMouse::SetSystemCursorPositionNormalized(AZ::Vector2 positionNormalized)
{
if (m_pimpl)
{
m_pimpl->SetSystemCursorPositionNormalized(positionNormalized);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZ::Vector2 InputDeviceMouse::GetSystemCursorPositionNormalized() const
{
return m_pimpl ? m_pimpl->GetSystemCursorPositionNormalized() : AZ::Vector2::CreateZero();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMouse::SetRawMovementSampleRate(AZ::u32 sampleRateHertz)
{
if (m_pimpl)
{
m_pimpl->SetRawMovementSampleRate(sampleRateHertz);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceMouse::Implementation::Implementation(InputDeviceMouse& inputDevice)
: m_inputDevice(inputDevice)
, m_rawMovementSampleRate()
, m_rawButtonEventQueuesById()
, m_rawMovementEventQueuesById()
, m_timeOfLastRawMovementSample(AZStd::chrono::system_clock::now())
{
SetRawMovementSampleRate(MovementSampleRateDefault);
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceMouse::Implementation::~Implementation()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMouse::Implementation::QueueRawButtonEvent(const InputChannelId& inputChannelId,
bool rawButtonState)
{
// It should not (in theory) be possible to receive multiple button events with the same id
// and state in succession; if it happens in practice for whatever reason this is still safe.
m_rawButtonEventQueuesById[inputChannelId].push_back(rawButtonState);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMouse::Implementation::QueueRawMovementEvent(const InputChannelId& inputChannelId,
float rawMovementDelta)
{
auto now = AZStd::chrono::system_clock::now();
auto deltaTime = now - m_timeOfLastRawMovementSample;
auto& rawEventQueue = m_rawMovementEventQueuesById[inputChannelId];
// Depending on the movement sample rate, multiple mouse movements within a frame are either:
if (rawEventQueue.empty() || deltaTime.count() > m_rawMovementSampleRate)
{
// queued (to give a better response at low frame rates)
rawEventQueue.push_back(rawMovementDelta);
m_timeOfLastRawMovementSample = now;
}
else
{
// or accumulated (to avoid flooding the event queue)
rawEventQueue.back() += rawMovementDelta;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMouse::Implementation::ProcessRawEventQueues()
{
// Update the shared cursor position data
const AZ::Vector2 oldNormalizedPosition = m_inputDevice.m_cursorPositionData2D->m_normalizedPosition;
const AZ::Vector2 newNormalizedPosition = GetSystemCursorPositionNormalized();
m_inputDevice.m_cursorPositionData2D->m_normalizedPosition = newNormalizedPosition;
m_inputDevice.m_cursorPositionData2D->m_normalizedPositionDelta = newNormalizedPosition - oldNormalizedPosition;
// Process all raw input events that were queued since the last call to this function
ProcessRawInputEventQueues(m_rawButtonEventQueuesById, m_inputDevice.m_buttonChannelsById);
ProcessRawInputEventQueues(m_rawMovementEventQueuesById, m_inputDevice.m_movementChannelsById);
// Mouse movement events are distinct in that we may not receive an 'ended' event with delta
// value of zero when the mouse stops moving, so queueing one here ensures the channels will
// always correctly transition into the 'ended' state the next time this function is called,
// unless another movement delta is queued above in which case it will simply be added to 0.
for (const InputChannelId& movementChannelId : Movement::All)
{
QueueRawMovementEvent(movementChannelId, 0.0f);
}
// Finally, update the cursor position input channel, treating it as active if it has moved
const float distanceMoved = newNormalizedPosition.GetDistance(oldNormalizedPosition);
m_inputDevice.m_cursorPositionChannel->ProcessRawInputEvent(distanceMoved);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMouse::Implementation::ResetInputChannelStates()
{
m_inputDevice.ResetInputChannelStates();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMouse::Implementation::SetRawMovementSampleRate(AZ::u32 sampleRateHertz)
{
// Guard against dividing by zero
if (sampleRateHertz == MovementSampleRateAccumulateAll)
{
m_rawMovementSampleRate = static_cast<AZStd::sys_time_t>(std::numeric_limits<AZ::u32>::max());
}
else
{
m_rawMovementSampleRate = static_cast<AZStd::sys_time_t>(1000000 / sampleRateHertz);
}
}
} // namespace AzFramework
@@ -0,0 +1,305 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Buses/Requests/InputSystemCursorRequestBus.h>
#include <AzFramework/Input/Devices/InputDevice.h>
#include <AzFramework/Input/Channels/InputChannelDeltaWithSharedPosition2D.h>
#include <AzFramework/Input/Channels/InputChannelDigitalWithSharedPosition2D.h>
#include <AzCore/std/chrono/clocks.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Defines a generic mouse input device, including the ids of all its associated input channels.
//! Platform specific implementations are defined as private implementations so that creating an
//! instance of this generic class will work correctly on any platform that supports mouse input,
//! while providing access to the device name and associated channel ids on any platform through
//! the 'null' implementation (primarily so that the editor can use them to setup input mappings).
class InputDeviceMouse : public InputDevice,
public InputSystemCursorRequestBus::Handler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Default sample rate for raw mouse movement events that aims to strike a balance between
//! responsiveness and performance.
static const AZ::u32 MovementSampleRateDefault;
////////////////////////////////////////////////////////////////////////////////////////////
//! Sample rate for raw mouse movement that will cause all events received in the same frame
//! to be queued and dispatched as individual events. This results in maximum responsiveness
//! but may potentially impact performance depending how many events happen over each frame.
static const AZ::u32 MovementSampleRateQueueAll;
////////////////////////////////////////////////////////////////////////////////////////////
//! Sample rate for raw mouse movement that will cause all events received in the same frame
//! to be accumulated and dispatched as a single event. Optimal for performance, but results
//! in sluggish/unresponsive mouse movement, especially when running at low frame rates.
static const AZ::u32 MovementSampleRateAccumulateAll;
////////////////////////////////////////////////////////////////////////////////////////////
//! The id used to identify the primary mouse input device
static const InputDeviceId Id;
////////////////////////////////////////////////////////////////////////////////////////////
//! Check whether an input device id identifies a mouse (regardless of index)
//! \param[in] inputDeviceId The input device id to check
//! \return True if the input device id identifies a mouse, false otherwise
static bool IsMouseDevice(const InputDeviceId& inputDeviceId);
////////////////////////////////////////////////////////////////////////////////////////////
//! All the input channel ids that identify standard mouse buttons. Though some mice support
//! more than 5 buttons, it would be strange for a game to explicitly map them as this would
//! exclude the majority of players who use a regular 3-button mouse. Developers should most
//! likely expect players to assign additional mouse buttons to keyboard keys using software.
//!
//! Additionally, macOSX only supports three mouse buttons (left, right, and middle), so any
//! cross-platform game should entirely ignore the 'Other1' and 'Other2' buttons, which have
//! been implemented for windows simply to provide for backwards compatibility with CryInput.
struct Button
{
static const InputChannelId Left; //!< The left mouse button
static const InputChannelId Right; //!< The right mouse button
static const InputChannelId Middle; //!< The middle mouse button
static const InputChannelId Other1; //!< DEPRECATED: the x1 mouse button
static const InputChannelId Other2; //!< DEPRECATED: the x2 mouse button
//!< All mouse button ids
static const AZStd::array<InputChannelId, 5> All;
};
////////////////////////////////////////////////////////////////////////////////////////////
//! All the input channel ids that identify mouse movement. These input channels represent
//! raw mouse movement before any system cursor ballistics have been applied, and so don't
//! directly correlate to the mouse position (which is queried directly from the system).
struct Movement
{
static const InputChannelId X; //!< Raw horizontal mouse movement over the last frame
static const InputChannelId Y; //!< Raw vertical mouse movement over the last frame
static const InputChannelId Z; //!< Raw mouse wheel movement over the last frame
//!< All mouse movement ids
static const AZStd::array<InputChannelId, 3> All;
};
////////////////////////////////////////////////////////////////////////////////////////////
//! Input channel id of the system cursor position normalized relative to the active window.
//! The position obtained has had os ballistics applied, and is valid regardless of whether
//! the system cursor is hidden or visible. When the system cursor has been constrained to
//! the active window values will be in the [0.0, 1.0] range, but not when unconstrained.
//! See also InputSystemCursorRequests::SetSystemCursorState and GetSystemCursorState.
static const InputChannelId SystemCursorPosition;
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputDeviceMouse, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputDeviceMouse, "{A509CA9D-BEAA-4124-9AAD-7381E46EBDD4}", InputDevice);
////////////////////////////////////////////////////////////////////////////////////////////
// Reflection
static void Reflect(AZ::ReflectContext* context);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
explicit InputDeviceMouse();
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputDeviceMouse);
////////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
~InputDeviceMouse() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::GetInputChannelsById
const InputChannelByIdMap& GetInputChannelsById() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::IsSupported
bool IsSupported() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::IsConnected
bool IsConnected() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDeviceRequests::TickInputDevice
void TickInputDevice() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputSystemCursorRequests::SetSystemCursorState
void SetSystemCursorState(SystemCursorState systemCursorState) override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputSystemCursorRequests::GetSystemCursorState
SystemCursorState GetSystemCursorState() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputSystemCursorRequests::SetSystemCursorPositionNormalized
void SetSystemCursorPositionNormalized(AZ::Vector2 positionNormalized) override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputSystemCursorRequests::GetSystemCursorPositionNormalized
AZ::Vector2 GetSystemCursorPositionNormalized() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Set the sample rate for raw mouse movement events
//! \param[in] sampleRateHertz The raw movement sample rate in Hertz (cycles per second)
void SetRawMovementSampleRate(AZ::u32 sampleRateHertz);
protected:
////////////////////////////////////////////////////////////////////////////////////////////
///@{
//! Alias for verbose container class
using ButtonChannelByIdMap = AZStd::unordered_map<InputChannelId,
InputChannelDigitalWithSharedPosition2D*>;
using MovementChannelByIdMap = AZStd::unordered_map<InputChannelId,
InputChannelDeltaWithSharedPosition2D*>;
///@}
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
InputChannelByIdMap m_allChannelsById; //!< All mouse channels
ButtonChannelByIdMap m_buttonChannelsById; //!< Mouse button channels
MovementChannelByIdMap m_movementChannelsById; //!< Mouse movement channels
InputChannelDeltaWithSharedPosition2D* m_cursorPositionChannel; //!< Cursor position channel
InputChannel::SharedPositionData2D m_cursorPositionData2D; //!< Shared cursor position
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Base class for platform specific implementations of mouse input devices
class Implementation
{
public:
////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(Implementation, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////
//! Default factory create function
//! \param[in] inputDevice Reference to the input device being implemented
static Implementation* Create(InputDeviceMouse& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputDevice Reference to the input device being implemented
Implementation(InputDeviceMouse& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(Implementation);
////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
virtual ~Implementation();
////////////////////////////////////////////////////////////////////////////////////////
//! Query the connected state of the input device
//! \return True if the input device is currently connected, false otherwise
virtual bool IsConnected() const = 0;
////////////////////////////////////////////////////////////////////////////////////////
//! Attempt to set the state of the system cursor
//! \param[in] systemCursorState The desired system cursor state
virtual void SetSystemCursorState(SystemCursorState systemCursorState) = 0;
////////////////////////////////////////////////////////////////////////////////////////
//! Get the current state of the system cursor
//! \return The current state of the system cursor
virtual SystemCursorState GetSystemCursorState() const = 0;
////////////////////////////////////////////////////////////////////////////////////////
//! Attempt to set the system cursor position normalized relative to the active window
//! \param[in] positionNormalized The desired system cursor position normalized
virtual void SetSystemCursorPositionNormalized(AZ::Vector2 positionNormalized) = 0;
////////////////////////////////////////////////////////////////////////////////////////
//! Get the current system cursor position normalized relative to the active window. The
//! position obtained has had os ballistics applied, and is valid regardless of whether
//! the system cursor is hidden or visible. When the cursor has been constrained to the
//! active window the values will be in the [0.0, 1.0] range, but not when unconstrained.
//! See also InputSystemCursorRequests::SetSystemCursorState and GetSystemCursorState.
//! \return The current system cursor position normalized relative to the active window
virtual AZ::Vector2 GetSystemCursorPositionNormalized() const = 0;
////////////////////////////////////////////////////////////////////////////////////////
//! Tick/update the input device to broadcast all input events since the last frame
virtual void TickInputDevice() = 0;
////////////////////////////////////////////////////////////////////////////////////////
//! Set the sample rate for raw mouse movement events
//! \param[in] sampleRateHertz The raw movement sample rate in Hertz (cycles per second)
void SetRawMovementSampleRate(AZ::u32 sampleRateHertz);
protected:
////////////////////////////////////////////////////////////////////////////////////////
//! Queue raw button events to be processed in the next call to ProcessRawEventQueues.
//! This function is not thread safe and so should only be called from the main thread.
//! \param[in] inputChannelId The input channel id
//! \param[in] rawButtonState The raw button state
void QueueRawButtonEvent(const InputChannelId& inputChannelId, bool rawButtonState);
////////////////////////////////////////////////////////////////////////////////////////
//! Queue raw movement events to be processed in the next call to ProcessRawEventQueues.
//! This function is not thread safe and so should only be called from the main thread.
//! \param[in] inputChannelId The input channel id
//! \param[in] rawMovementDelta The raw movement delta
void QueueRawMovementEvent(const InputChannelId& inputChannelId, float rawMovementDelta);
////////////////////////////////////////////////////////////////////////////////////////
//! Process raw input events that have been queued since the last call to this function.
//! This function is not thread safe, and so should only be called from the main thread.
void ProcessRawEventQueues();
////////////////////////////////////////////////////////////////////////////////////////
//! Reset the state of all this input device's associated input channels
void ResetInputChannelStates();
////////////////////////////////////////////////////////////////////////////////////////
//! Alias for verbose container class
///@{
using RawButtonEventQueueByIdMap = AZStd::unordered_map<InputChannelId, AZStd::vector<bool>>;
using RawMovementEventQueueByIdMap = AZStd::unordered_map<InputChannelId, AZStd::vector<float>>;
///@}
private:
////////////////////////////////////////////////////////////////////////////////////////
// Variables
InputDeviceMouse& m_inputDevice; //!< Reference to the device
AZStd::sys_time_t m_rawMovementSampleRate; //!< Raw movement sample rate
RawButtonEventQueueByIdMap m_rawButtonEventQueuesById; //!< Raw button events by id
RawMovementEventQueueByIdMap m_rawMovementEventQueuesById; //!< Raw movement events by id
AZStd::chrono::system_clock::time_point m_timeOfLastRawMovementSample; //!< Time of the last raw movement sample
};
////////////////////////////////////////////////////////////////////////////////////////////
//! Set the implementation of this input device
//! \param[in] implementation The new implementation
void SetImplementation(AZStd::unique_ptr<Implementation> impl) { m_pimpl = AZStd::move(impl); }
private:
////////////////////////////////////////////////////////////////////////////////////////////
//! Private pointer to the platform specific implementation
AZStd::unique_ptr<Implementation> m_pimpl;
////////////////////////////////////////////////////////////////////////////////////////////
//! Helper class that handles requests to create a custom implementation for this device
InputDeviceImplementationRequestHandler<InputDeviceMouse> m_implementationRequestHandler;
};
} // namespace AzFramework
@@ -0,0 +1,239 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Devices/Touch/InputDeviceTouch.h>
#include <AzFramework/Input/Utils/ProcessRawInputEventQueues.h>
#include <AzCore/Debug/Trace.h>
#include <AzCore/RTTI/BehaviorContext.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
const InputDeviceId InputDeviceTouch::Id("touch");
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceTouch::IsTouchDevice(const InputDeviceId& inputDeviceId)
{
return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32());
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceTouch::Touch::Index0("touch_index_0");
const InputChannelId InputDeviceTouch::Touch::Index1("touch_index_1");
const InputChannelId InputDeviceTouch::Touch::Index2("touch_index_2");
const InputChannelId InputDeviceTouch::Touch::Index3("touch_index_3");
const InputChannelId InputDeviceTouch::Touch::Index4("touch_index_4");
const InputChannelId InputDeviceTouch::Touch::Index5("touch_index_5");
const InputChannelId InputDeviceTouch::Touch::Index6("touch_index_6");
const InputChannelId InputDeviceTouch::Touch::Index7("touch_index_7");
const InputChannelId InputDeviceTouch::Touch::Index8("touch_index_8");
const InputChannelId InputDeviceTouch::Touch::Index9("touch_index_9");
const AZStd::array<InputChannelId, 10> InputDeviceTouch::Touch::All =
{{
Index0,
Index1,
Index2,
Index3,
Index4,
Index5,
Index6,
Index7,
Index8,
Index9
}};
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceTouch::Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
// Unfortunately it doesn't seem possible to reflect anything through BehaviorContext
// using lambdas which capture variables from the enclosing scope. So we are manually
// reflecting all input channel names, instead of just iterating over them like this:
//
// auto classBuilder = behaviorContext->Class<InputDeviceTouch>();
// for (const InputChannelId& channelId : Touch::All)
// {
// const char* channelName = channelId.GetName();
// classBuilder->Constant(channelName, [channelName]() { return channelName; });
// }
behaviorContext->Class<InputDeviceTouch>()
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::RuntimeOwn)
->Constant("name", BehaviorConstant(Id.GetName()))
->Constant(Touch::Index0.GetName(), BehaviorConstant(Touch::Index0.GetName()))
->Constant(Touch::Index1.GetName(), BehaviorConstant(Touch::Index1.GetName()))
->Constant(Touch::Index2.GetName(), BehaviorConstant(Touch::Index2.GetName()))
->Constant(Touch::Index3.GetName(), BehaviorConstant(Touch::Index3.GetName()))
->Constant(Touch::Index4.GetName(), BehaviorConstant(Touch::Index4.GetName()))
->Constant(Touch::Index5.GetName(), BehaviorConstant(Touch::Index5.GetName()))
->Constant(Touch::Index6.GetName(), BehaviorConstant(Touch::Index6.GetName()))
->Constant(Touch::Index7.GetName(), BehaviorConstant(Touch::Index7.GetName()))
->Constant(Touch::Index8.GetName(), BehaviorConstant(Touch::Index8.GetName()))
->Constant(Touch::Index9.GetName(), BehaviorConstant(Touch::Index9.GetName()))
;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceTouch::InputDeviceTouch()
: InputDevice(Id)
, m_allChannelsById()
, m_touchChannelsById()
, m_pimpl(nullptr)
, m_implementationRequestHandler(*this)
{
// Create all touch input channels
for (AZ::u32 i = 0; i < Touch::All.size(); ++i)
{
const InputChannelId& channelId = Touch::All[i];
InputChannelAnalogWithPosition2D* channel = aznew InputChannelAnalogWithPosition2D(channelId, *this);
m_allChannelsById[channelId] = channel;
m_touchChannelsById[channelId] = channel;
}
// Create the platform specific implementation
m_pimpl.reset(Implementation::Create(*this));
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceTouch::~InputDeviceTouch()
{
// Destroy the platform specific implementation
m_pimpl.reset();
// Destroy all touch input channels
for (const auto& channelById : m_touchChannelsById)
{
delete channelById.second;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
LocalUserId InputDeviceTouch::GetAssignedLocalUserId() const
{
return m_pimpl ? m_pimpl->GetAssignedLocalUserId() : InputDevice::GetAssignedLocalUserId();
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputDevice::InputChannelByIdMap& InputDeviceTouch::GetInputChannelsById() const
{
return m_allChannelsById;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceTouch::IsSupported() const
{
return m_pimpl != nullptr;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceTouch::IsConnected() const
{
return m_pimpl ? m_pimpl->IsConnected() : false;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceTouch::TickInputDevice()
{
if (m_pimpl)
{
m_pimpl->TickInputDevice();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceTouch::Implementation::Implementation(InputDeviceTouch& inputDevice)
: m_inputDevice(inputDevice)
, m_rawTouchEventQueuesById()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceTouch::Implementation::~Implementation()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
LocalUserId InputDeviceTouch::Implementation::GetAssignedLocalUserId() const
{
return m_inputDevice.GetInputDeviceId().GetIndex();
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceTouch::Implementation::RawTouchEvent::RawTouchEvent(float normalizedX,
float normalizedY,
float pressure,
AZ::u32 index,
State state)
: InputChannelAnalogWithPosition2D::RawInputEvent(normalizedX, normalizedY, pressure)
, m_index(index)
, m_state(state)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceTouch::Implementation::QueueRawTouchEvent(const RawTouchEvent& rawTouchEvent)
{
if (rawTouchEvent.m_index >= Touch::All.size())
{
AZ_Warning("InputDeviceTouch", false,
"Raw touch has index: %d that is >= max: %zd",
rawTouchEvent.m_index, Touch::All.size());
return;
}
auto& rawEventQueue = m_rawTouchEventQueuesById[Touch::All[rawTouchEvent.m_index]];
if (rawEventQueue.empty() || rawEventQueue.back().m_state != rawTouchEvent.m_state)
{
// No raw touches for this index have been queued this frame,
// or the last raw touch queued was in a different state.
rawEventQueue.push_back(rawTouchEvent);
}
else
{
// Because touches are sampled at a rate (~30-60fps) independent of the simulation
// (which is necessary for the controls to remain responsive), when the simulation
// frame rate drops we end up receiving multiple touch move events each frame, for
// the same finger, which (especially with multi-touch) can generate enough events
// to cause the simulation frame rate to drop even further. To combat this we will
// combine multiple touch events for the same finger if they are in the same state.
//
// For example, the following sequence of events with the same index in one frame:
// - Began, Moved, Moved, Ended, Began, Moved, Moved, Moved
//
// Will be collapsed as follows:
// - Began, Moved, Ended, Began, Moved
//
// This seems to maintain a good balance between responsiveness vs accuracy. While
// it should not (in theory) be possible to receive multiple Began or Ended events
// in succession, if it happens in practice for whatever reason this is still safe.
rawEventQueue.back() = rawTouchEvent;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceTouch::Implementation::ProcessRawEventQueues()
{
// Process all raw input events that were queued since the last call to this function.
ProcessRawInputEventQueues(m_rawTouchEventQueuesById, m_inputDevice.m_touchChannelsById);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceTouch::Implementation::ResetInputChannelStates()
{
m_inputDevice.ResetInputChannelStates();
}
} // namespace AzFramework
@@ -0,0 +1,233 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Devices/InputDevice.h>
#include <AzFramework/Input/Channels/InputChannelAnalogWithPosition2D.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Defines a generic touch input device, including the ids of all its associated input channels.
//! Platform specific implementations are defined as private implementations so that creating an
//! instance of this generic class will work correctly on any platform that supports touch input,
//! while providing access to the device name and associated channel ids on any platform through
//! the 'null' implementation (primarily so that the editor can use them to setup input mappings).
class InputDeviceTouch : public InputDevice
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! The id used to identify the primary touch input device
static const InputDeviceId Id;
////////////////////////////////////////////////////////////////////////////////////////////
//! Check whether an input device id identifies a touch device (regardless of index)
//! \param[in] inputDeviceId The input device id to check
//! \return True if the input device id identifies a touch device, false otherwise
static bool IsTouchDevice(const InputDeviceId& inputDeviceId);
////////////////////////////////////////////////////////////////////////////////////////////
//! All the input channel ids that identify touches. The maximum number of active touches to
//! track is arbitrary, but ten seems to be more than sufficient for most game applications.
struct Touch
{
static const InputChannelId Index0; //!< Touch index 0
static const InputChannelId Index1; //!< Touch index 1
static const InputChannelId Index2; //!< Touch index 2
static const InputChannelId Index3; //!< Touch index 3
static const InputChannelId Index4; //!< Touch index 4
static const InputChannelId Index5; //!< Touch index 5
static const InputChannelId Index6; //!< Touch index 6
static const InputChannelId Index7; //!< Touch index 7
static const InputChannelId Index8; //!< Touch index 8
static const InputChannelId Index9; //!< Touch index 9
//!< All touch input channel ids
static const AZStd::array<InputChannelId, 10> All;
};
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputDeviceTouch, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputDeviceTouch, "{796E4C57-4D6C-4DAA-8367-9026509E86EF}", InputDevice);
////////////////////////////////////////////////////////////////////////////////////////////
// Reflection
static void Reflect(AZ::ReflectContext* context);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
InputDeviceTouch();
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputDeviceTouch);
////////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
~InputDeviceTouch() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::GetAssignedLocalUserId
LocalUserId GetAssignedLocalUserId() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::GetInputChannelsById
const InputChannelByIdMap& GetInputChannelsById() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::IsSupported
bool IsSupported() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::IsConnected
bool IsConnected() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDeviceRequests::TickInputDevice
void TickInputDevice() override;
protected:
////////////////////////////////////////////////////////////////////////////////////////////
//! Alias for verbose container class
using TouchChannelByIdMap = AZStd::unordered_map<InputChannelId, InputChannelAnalogWithPosition2D*>;
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
InputChannelByIdMap m_allChannelsById; //!< All touch channels by id
TouchChannelByIdMap m_touchChannelsById; //!< All touch channels by id
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Base class for platform specific implementations of touch input devices
class Implementation
{
public:
////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(Implementation, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////
//! Default factory create function
//! \param[in] inputDevice Reference to the input device being implemented
static Implementation* Create(InputDeviceTouch& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputDevice Reference to the input device being implemented
Implementation(InputDeviceTouch& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(Implementation);
////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
virtual ~Implementation();
////////////////////////////////////////////////////////////////////////////////////////
//! Access to the input device's currently assigned local user id
//! \return Id of the local user currently assigned to the input device
virtual LocalUserId GetAssignedLocalUserId() const;
////////////////////////////////////////////////////////////////////////////////////////
//! Query the connected state of the input device
//! \return True if the input device is currently connected, false otherwise
virtual bool IsConnected() const = 0;
////////////////////////////////////////////////////////////////////////////////////////
//! Tick/update the input device to broadcast all input events since the last frame
virtual void TickInputDevice() = 0;
protected:
////////////////////////////////////////////////////////////////////////////////////////
//! Platform agnostic representation of a raw touch event
struct RawTouchEvent : public InputChannelAnalogWithPosition2D::RawInputEvent
{
////////////////////////////////////////////////////////////////////////////////////
//! State of the raw touch event
enum class State
{
Began,
Moved,
Ended
};
////////////////////////////////////////////////////////////////////////////////////
//! Constructor
explicit RawTouchEvent(
float normalizedX,
float normalizedY,
float pressure,
AZ::u32 index,
State state);
////////////////////////////////////////////////////////////////////////////////////
// Default copying
AZ_DEFAULT_COPY(RawTouchEvent);
////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
~RawTouchEvent() override = default;
////////////////////////////////////////////////////////////////////////////////////
// Variables
AZ::u32 m_index; //!< The index of the raw touch event
State m_state; //!< The state of the raw touch event
};
////////////////////////////////////////////////////////////////////////////////////////
//! Queue raw touch events to be processed in the next call to ProcessRawEventQueues.
//! This function is not thread safe and so should only be called from the main thread.
//! \param[in] rawTouchEvent The raw touch event
void QueueRawTouchEvent(const RawTouchEvent& rawTouchEvent);
////////////////////////////////////////////////////////////////////////////////////////
//! Process raw input events that have been queued since the last call to this function.
//! This function is not thread safe, and so should only be called from the main thread.
void ProcessRawEventQueues();
////////////////////////////////////////////////////////////////////////////////////////
//! Reset the state of all this input device's associated input channels
void ResetInputChannelStates();
////////////////////////////////////////////////////////////////////////////////////////
//! Alias for verbose container class
using RawTouchEventQueueByIdMap = AZStd::unordered_map<InputChannelId, AZStd::vector<RawTouchEvent>>;
private:
////////////////////////////////////////////////////////////////////////////////////////
// Variables
InputDeviceTouch& m_inputDevice; //!< Reference to the input device
RawTouchEventQueueByIdMap m_rawTouchEventQueuesById; //!< Raw touch event queues by id
};
////////////////////////////////////////////////////////////////////////////////////////////
//! Set the implementation of this input device
//! \param[in] implementation The new implementation
void SetImplementation(AZStd::unique_ptr<Implementation> impl) { m_pimpl = AZStd::move(impl); }
private:
////////////////////////////////////////////////////////////////////////////////////////////
//! Private pointer to the platform specific implementation
AZStd::unique_ptr<Implementation> m_pimpl;
////////////////////////////////////////////////////////////////////////////////////////////
//! Helper class that handles requests to create a custom implementation for this device
InputDeviceImplementationRequestHandler<InputDeviceTouch> m_implementationRequestHandler;
};
} // namespace AzFramework
@@ -0,0 +1,219 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h>
#include <AzFramework/Input/Utils/ProcessRawInputEventQueues.h>
#include <AzCore/RTTI/BehaviorContext.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
const InputDeviceId InputDeviceVirtualKeyboard::Id("virtual_keyboard");
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceVirtualKeyboard::IsVirtualKeyboardDevice(const InputDeviceId& inputDeviceId)
{
return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32());
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceVirtualKeyboard::Command::EditEnter("virtual_keyboard_edit_enter");
const InputChannelId InputDeviceVirtualKeyboard::Command::EditClear("virtual_keyboard_edit_clear");
const InputChannelId InputDeviceVirtualKeyboard::Command::NavigationBack("virtual_keyboard_navigation_back");
const AZStd::array<InputChannelId, 3> InputDeviceVirtualKeyboard::Command::All =
{{
EditClear,
EditEnter,
NavigationBack
}};
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceVirtualKeyboard::Reflect(AZ::ReflectContext* context)
{
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
// Unfortunately it doesn't seem possible to reflect anything through BehaviorContext
// using lambdas which capture variables from the enclosing scope. So we are manually
// reflecting all input channel names, instead of just iterating over them like this:
//
// auto classBuilder = behaviorContext->Class<InputDeviceVirtualKeyboard>();
// for (const InputChannelId& channelId : Command::All)
// {
// const char* channelName = channelId.GetName();
// classBuilder->Constant(channelName, [channelName]() { return channelName; });
// }
behaviorContext->Class<InputDeviceVirtualKeyboard>()
->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::RuntimeOwn)
->Constant("name", BehaviorConstant(Id.GetName()))
->Constant(Command::EditClear.GetName(), BehaviorConstant(Command::EditClear.GetName()))
->Constant(Command::EditEnter.GetName(), BehaviorConstant(Command::EditEnter.GetName()))
->Constant(Command::NavigationBack.GetName(), BehaviorConstant(Command::NavigationBack.GetName()))
;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceVirtualKeyboard::InputDeviceVirtualKeyboard()
: InputDevice(Id)
, m_allChannelsById()
, m_pimpl()
, m_implementationRequestHandler(*this)
{
// Create all command input channels
for (const InputChannelId& channelId : Command::All)
{
InputChannel* channel = aznew InputChannel(channelId, *this);
m_allChannelsById[channelId] = channel;
m_commandChannelsById[channelId] = channel;
}
// Create the platform specific implementation
m_pimpl.reset(Implementation::Create(*this));
// Connect to the text entry request bus
InputTextEntryRequestBus::Handler::BusConnect(GetInputDeviceId());
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceVirtualKeyboard::~InputDeviceVirtualKeyboard()
{
// Disconnect from the text entry request bus
InputTextEntryRequestBus::Handler::BusDisconnect(GetInputDeviceId());
// Destroy the platform specific implementation
m_pimpl.reset();
// Destroy all command input channels
for (const auto& channelById : m_commandChannelsById)
{
delete channelById.second;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputDevice::InputChannelByIdMap& InputDeviceVirtualKeyboard::GetInputChannelsById() const
{
return m_allChannelsById;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceVirtualKeyboard::IsSupported() const
{
return m_pimpl != nullptr;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceVirtualKeyboard::IsConnected() const
{
return m_pimpl ? m_pimpl->IsConnected() : false;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputDeviceVirtualKeyboard::HasTextEntryStarted() const
{
return m_pimpl ? m_pimpl->HasTextEntryStarted() : false;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceVirtualKeyboard::TextEntryStart(const VirtualKeyboardOptions& options)
{
if (m_pimpl)
{
m_pimpl->TextEntryStart(options);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceVirtualKeyboard::TextEntryStop()
{
if (m_pimpl)
{
m_pimpl->TextEntryStop();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceVirtualKeyboard::TickInputDevice()
{
if (m_pimpl)
{
m_pimpl->TickInputDevice();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceVirtualKeyboard::Implementation::Implementation(InputDeviceVirtualKeyboard& inputDevice)
: m_inputDevice(inputDevice)
, m_rawCommandEventQueue()
, m_rawTextEventQueue()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputDeviceVirtualKeyboard::Implementation::~Implementation()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceVirtualKeyboard::Implementation::QueueRawCommandEvent(
const InputChannelId& inputChannelId)
{
// Virtual keyboard commands are unique in that they don't go through states like most
// other input channels. Rather, they simply dispatch one-off 'fire and forget' events.
// But we still want to queue them so that they're dispatched in ProcessRawEventQueues
// at the same as all other input events during the call to TickInputDevice each frame.
m_rawCommandEventQueue.push_back(inputChannelId);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceVirtualKeyboard::Implementation::QueueRawTextEvent(const AZStd::string& textUTF8)
{
m_rawTextEventQueue.push_back(textUTF8);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceVirtualKeyboard::Implementation::ProcessRawEventQueues()
{
// Process all raw input events that were queued since the last call to this function.
// Text events should be processed first in case text input is disabled by a command event.
ProcessRawInputTextEventQueue(m_rawTextEventQueue);
// Virtual keyboard commands are unique in that they don't go through states like most
// other input channels. Rather, they simply dispatch one-off 'fire and forget' events.
for (const InputChannelId& channelId : m_rawCommandEventQueue)
{
const auto& channelIt = m_inputDevice.m_commandChannelsById.find(channelId);
if (channelIt != m_inputDevice.m_commandChannelsById.end() && channelIt->second)
{
const InputChannel& channel = *(channelIt->second);
m_inputDevice.BroadcastInputChannelEvent(channel);
}
else
{
// Unknown channel id, warn but handle gracefully
AZ_Warning("InputDeviceVirtualKeyboard::Implementation::ProcessRawEventQueues", false,
"Raw input event queued with unrecognized id: %s", channelId.GetName());
}
}
m_rawCommandEventQueue.clear();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceVirtualKeyboard::Implementation::ResetInputChannelStates()
{
m_inputDevice.ResetInputChannelStates();
}
} // namespace AzFramework
@@ -0,0 +1,216 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Buses/Requests/InputTextEntryRequestBus.h>
#include <AzFramework/Input/Channels/InputChannel.h>
#include <AzFramework/Input/Devices/InputDevice.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Defines a generic virtual keyboard device. Platform specific implementations are defined as
//! private implementations so that creating an instance of this generic class will work on any
//! platform that supports a virtual keyboard.
class InputDeviceVirtualKeyboard : public InputDevice
, public InputTextEntryRequestBus::Handler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! The id used to identify the primary virtual keyboard input device
static const InputDeviceId Id;
////////////////////////////////////////////////////////////////////////////////////////////
//! Check whether an input device id identifies a virtual keyboard (regardless of index)
//! \param[in] inputDeviceId The input device id to check
//! \return True if the input device id identifies a virtual keyboard, false otherwise
static bool IsVirtualKeyboardDevice(const InputDeviceId& inputDeviceId);
////////////////////////////////////////////////////////////////////////////////////////////
//! All the input channel ids that identify virtual keyboard commands which systems may need
//! to respond to.
struct Command
{
//!< The clear command used to indicate the user wants to clear the active text field
static const InputChannelId EditClear;
//!< The enter/return/close command used to indicate the user has finished text editing
static const InputChannelId EditEnter;
//!< The back command used to indicate the user wants to navigate 'backwards'.
//!< This is specific to android devices, and does not have an ios equivalent.
static const InputChannelId NavigationBack;
//!< All virtual keyboard command ids
static const AZStd::array<InputChannelId, 3> All;
};
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputDeviceVirtualKeyboard, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputDeviceVirtualKeyboard, "{85BA81F4-EB74-4CFB-8504-DD8C555D8D79}", InputDevice);
////////////////////////////////////////////////////////////////////////////////////////////
// Reflection
static void Reflect(AZ::ReflectContext* context);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
InputDeviceVirtualKeyboard();
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputDeviceVirtualKeyboard);
////////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
~InputDeviceVirtualKeyboard() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::GetInputChannelsById
const InputChannelByIdMap& GetInputChannelsById() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::IsSupported
bool IsSupported() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDevice::IsConnected
bool IsConnected() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputTextEntryRequests::HasTextEntryStarted
bool HasTextEntryStarted() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputTextEntryRequests::TextEntryStart
void TextEntryStart(const VirtualKeyboardOptions& options) override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputTextEntryRequests::TextEntryStop
void TextEntryStop() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputDeviceRequests::TickInputDevice
void TickInputDevice() override;
protected:
////////////////////////////////////////////////////////////////////////////////////////////
//! Alias for verbose container class
using CommandChannelByIdMap = AZStd::unordered_map<InputChannelId, InputChannel*>;
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
InputChannelByIdMap m_allChannelsById; //!< All virtual keyboard channels by id
CommandChannelByIdMap m_commandChannelsById; //!< All virtual command channels by id
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Base class for platform specific implementations of virtual keyboard input devices
class Implementation
{
public:
////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(Implementation, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////
//! Default factory create function
//! \param[in] inputDevice Reference to the input device being implemented
static Implementation* Create(InputDeviceVirtualKeyboard& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputDevice Reference to the input device being implemented
Implementation(InputDeviceVirtualKeyboard& inputDevice);
////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(Implementation);
////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
virtual ~Implementation();
////////////////////////////////////////////////////////////////////////////////////////
//! Query the connected state of the input device
//! \return True if the input device is currently connected, false otherwise
virtual bool IsConnected() const = 0;
////////////////////////////////////////////////////////////////////////////////////////
//! Query whether the virtual keyboard is being displayed/text input enabled
//! \return True if the virtual keyboard is being displayed, false otherwise
virtual bool HasTextEntryStarted() const = 0;
////////////////////////////////////////////////////////////////////////////////////////
//! Display the virtual keyboard, enabling text input (pair with StopTextInput)
//! \param[in] options Used to specify the appearance/behavior of the virtual keyboard
virtual void TextEntryStart(const VirtualKeyboardOptions& options) = 0;
////////////////////////////////////////////////////////////////////////////////////////
//! Hide the virtual keyboard, disabling text input (pair with StartTextInput)
virtual void TextEntryStop() = 0;
////////////////////////////////////////////////////////////////////////////////////////
//! Tick/update the input device to broadcast all input events since the last frame
virtual void TickInputDevice() = 0;
////////////////////////////////////////////////////////////////////////////////////////
//! Queue raw command event to be processed in the next call to ProcessRawEventQueues.
//! This function is not thread safe and so should only be called from the main thread.
//! \param[in] inputChannelId The input channel id
void QueueRawCommandEvent(const InputChannelId& inputChannelId);
////////////////////////////////////////////////////////////////////////////////////////
//! Queue raw text events to be processed in the next call to ProcessRawEventQueues.
//! This function is not thread safe and so should only be called from the main thread.
//! \param[in] textUTF8 The text to queue (encoded using UTF-8)
void QueueRawTextEvent(const AZStd::string& textUTF8);
protected:
////////////////////////////////////////////////////////////////////////////////////////
//! Process raw input events that have been queued since the last call to this function.
//! This function is not thread safe, and so should only be called from the main thread.
void ProcessRawEventQueues();
////////////////////////////////////////////////////////////////////////////////////////
//! Reset the state of all this input device's associated input channels
void ResetInputChannelStates();
private:
////////////////////////////////////////////////////////////////////////////////////////
// Variables
InputDeviceVirtualKeyboard& m_inputDevice; //!< Reference to the input device
AZStd::vector<InputChannelId> m_rawCommandEventQueue; //!< The raw command event queue
AZStd::vector<AZStd::string> m_rawTextEventQueue; //!< The raw text event queue
};
////////////////////////////////////////////////////////////////////////////////////////////
//! Set the implementation of this input device
//! \param[in] implementation The new implementation
void SetImplementation(AZStd::unique_ptr<Implementation> impl) { m_pimpl = AZStd::move(impl); }
private:
////////////////////////////////////////////////////////////////////////////////////////////
//! Private pointer to the platform specific implementation
AZStd::unique_ptr<Implementation> m_pimpl;
////////////////////////////////////////////////////////////////////////////////////////////
//! Helper class that handles requests to create a custom implementation for this device
InputDeviceImplementationRequestHandler<InputDeviceVirtualKeyboard> m_implementationRequestHandler;
};
} // namespace AzFramework
@@ -0,0 +1,156 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Events/InputChannelEventFilter.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
const AZ::Crc32 InputChannelEventFilter::AnyChannelNameCrc32("wildcard_any_input_channel_name");
const AZ::Crc32 InputChannelEventFilter::AnyDeviceNameCrc32("wildcard_any_input_device_name");
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelEventFilterInclusionList::InputChannelEventFilterInclusionList(
const AZ::Crc32& channelNameCrc32, // AnyChannelNameCrc32
const AZ::Crc32& deviceNameCrc32, // AnyDeviceNameCrc32
const LocalUserId& localUserId) // LocalUserIdAny
: m_channelNameCrc32InclusionList()
, m_deviceNameCrc32InclusionList()
, m_localUserIdInclusionList()
{
IncludeChannelName(channelNameCrc32);
IncludeDeviceName(deviceNameCrc32);
IncludeLocalUserId(localUserId);
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputChannelEventFilterInclusionList::DoesPassFilter(const InputChannel& inputChannel) const
{
if (!m_channelNameCrc32InclusionList.empty())
{
const AZ::Crc32& channelName = inputChannel.GetInputChannelId().GetNameCrc32();
if (m_channelNameCrc32InclusionList.find(channelName) == m_channelNameCrc32InclusionList.end())
{
return false;
}
}
if (!m_deviceNameCrc32InclusionList.empty())
{
const AZ::Crc32& deviceName = inputChannel.GetInputDevice().GetInputDeviceId().GetNameCrc32();
if (m_deviceNameCrc32InclusionList.find(deviceName) == m_deviceNameCrc32InclusionList.end())
{
return false;
}
}
if (!m_localUserIdInclusionList.empty())
{
const LocalUserId localUserId = inputChannel.GetInputDevice().GetAssignedLocalUserId();
if (m_localUserIdInclusionList.find(localUserId) == m_localUserIdInclusionList.end())
{
return false;
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelEventFilterInclusionList::IncludeChannelName(const AZ::Crc32& channelNameCrc32)
{
if (channelNameCrc32 == AnyChannelNameCrc32)
{
m_channelNameCrc32InclusionList.clear();
}
else
{
m_channelNameCrc32InclusionList.insert(channelNameCrc32);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelEventFilterInclusionList::IncludeDeviceName(const AZ::Crc32& deviceNameCrc32)
{
if (deviceNameCrc32 == AnyDeviceNameCrc32)
{
m_deviceNameCrc32InclusionList.clear();
}
else
{
m_deviceNameCrc32InclusionList.insert(deviceNameCrc32);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelEventFilterInclusionList::IncludeLocalUserId(LocalUserId localUserId)
{
if (localUserId == LocalUserIdAny)
{
m_localUserIdInclusionList.clear();
}
else
{
m_localUserIdInclusionList.insert(localUserId);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelEventFilterExclusionList::InputChannelEventFilterExclusionList()
: m_channelNameCrc32ExclusionList()
, m_deviceNameCrc32ExclusionList()
, m_localUserIdExclusionList()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputChannelEventFilterExclusionList::DoesPassFilter(const InputChannel& inputChannel) const
{
const AZ::Crc32& channelName = inputChannel.GetInputChannelId().GetNameCrc32();
if (m_channelNameCrc32ExclusionList.find(channelName) != m_channelNameCrc32ExclusionList.end())
{
return false;
}
const AZ::Crc32& deviceName = inputChannel.GetInputDevice().GetInputDeviceId().GetNameCrc32();
if (m_deviceNameCrc32ExclusionList.find(deviceName) != m_deviceNameCrc32ExclusionList.end())
{
return false;
}
const LocalUserId localUserId = inputChannel.GetInputDevice().GetAssignedLocalUserId();
if (m_localUserIdExclusionList.find(localUserId) != m_localUserIdExclusionList.end())
{
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelEventFilterExclusionList::ExcludeChannelName(const AZ::Crc32& channelNameCrc32)
{
m_channelNameCrc32ExclusionList.insert(channelNameCrc32);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelEventFilterExclusionList::ExcludeDeviceName(const AZ::Crc32& deviceNameCrc32)
{
m_deviceNameCrc32ExclusionList.insert(deviceNameCrc32);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelEventFilterExclusionList::ExcludeLocalUserId(LocalUserId localUserId)
{
m_localUserIdExclusionList.insert(localUserId);
}
} // namespace AzFramework
@@ -0,0 +1,145 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Channels/InputChannel.h>
#include <AzFramework/Input/Devices/InputDevice.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Class that filters input events by channel name, device name, local user id, or
//! any combination of the three.
class InputChannelEventFilter
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Wildcard representing any input channel name
static const AZ::Crc32 AnyChannelNameCrc32;
////////////////////////////////////////////////////////////////////////////////////////////
//! Wildcard representing any input device name
static const AZ::Crc32 AnyDeviceNameCrc32;
////////////////////////////////////////////////////////////////////////////////////////////
//! Default constructor
InputChannelEventFilter() = default;
////////////////////////////////////////////////////////////////////////////////////////////
// Default copying
AZ_DEFAULT_COPY(InputChannelEventFilter);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
virtual ~InputChannelEventFilter() = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Check whether an input channel should pass through the filter
//! \param[in] inputChannel The input channel to be filtered
//! \return True if the input channel passes the filter, false otherwise
virtual bool DoesPassFilter(const InputChannel& inputChannel) const = 0;
};
////////////////////////////////////////////////////////////////////////////////////////////////
//! Class that filters input channel events based on included input channels and devices.
class InputChannelEventFilterInclusionList : public InputChannelEventFilter
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor. By default the filter will be constructed to include all input events.
//! \param[in] channelNameCrc32 The input channel name (Crc32) to include (default any)
//! \param[in] deviceNameCrc32 The input device name (Crc32) to include (default any)
//! \param[in] localUserId The local user id to include (default any)
explicit InputChannelEventFilterInclusionList(const AZ::Crc32& channelNameCrc32 = AnyChannelNameCrc32,
const AZ::Crc32& deviceNameCrc32 = AnyDeviceNameCrc32,
const LocalUserId& localUserId = LocalUserIdAny);
////////////////////////////////////////////////////////////////////////////////////////////
// Default copying
AZ_DEFAULT_COPY(InputChannelEventFilterInclusionList);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
~InputChannelEventFilterInclusionList() override = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelEventFilter::DoesPassFilter
bool DoesPassFilter(const InputChannel& inputChannel) const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Add an input channel name to the inclusion list
//! \param[in] channelNameCrc32 The input channel name (Crc32) to add to the inclusion list
void IncludeChannelName(const AZ::Crc32& channelNameCrc32);
////////////////////////////////////////////////////////////////////////////////////////////
//! Add an input device name to the inclusion list
//! \param[in] channelNameCrc32 The input device name (Crc32) to add to the inclusion list
void IncludeDeviceName(const AZ::Crc32& deviceNameCrc32);
////////////////////////////////////////////////////////////////////////////////////////////
//! Add a local user id to the inclusion list
//! \param[in] localUserId The local user id to add to the inclusion list
void IncludeLocalUserId(LocalUserId localUserId);
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
AZStd::unordered_set<AZ::Crc32> m_channelNameCrc32InclusionList; //!< Channel name inclusion list
AZStd::unordered_set<AZ::Crc32> m_deviceNameCrc32InclusionList; //!< Device name inclusion list
AZStd::unordered_set<LocalUserId> m_localUserIdInclusionList; //!< Local user inclusion list
};
////////////////////////////////////////////////////////////////////////////////////////////////
//! Class that filters input channel events based on excluded input channels and devices.
class InputChannelEventFilterExclusionList : public InputChannelEventFilter
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor. By default the filter will be constructed to exclude no input events.
InputChannelEventFilterExclusionList();
////////////////////////////////////////////////////////////////////////////////////////////
// Default copying
AZ_DEFAULT_COPY(InputChannelEventFilterExclusionList);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
~InputChannelEventFilterExclusionList() override = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelEventFilter::DoesPassFilter
bool DoesPassFilter(const InputChannel& inputChannel) const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Add an input channel name to the exclusion list
//! \param[in] channelNameCrc32 The input channel name (Crc32) to add to the exclusion list
void ExcludeChannelName(const AZ::Crc32& channelNameCrc32);
////////////////////////////////////////////////////////////////////////////////////////////
//! Add an input device name to the exclusion list
//! \param[in] channelNameCrc32 The input device name (Crc32) to add to the exclusion list
void ExcludeDeviceName(const AZ::Crc32& deviceNameCrc32);
////////////////////////////////////////////////////////////////////////////////////////////
//! Add a local user id to the exclusion list
//! \param[in] localUserId The local user id to to add to the exclusion list
void ExcludeLocalUserId(LocalUserId localUserId);
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
AZStd::unordered_set<AZ::Crc32> m_channelNameCrc32ExclusionList; //!< Channel name exclusion list
AZStd::unordered_set<AZ::Crc32> m_deviceNameCrc32ExclusionList; //!< Device name exclusion list
AZStd::unordered_set<LocalUserId> m_localUserIdExclusionList; //!< Local user exclusion list
};
} // namespace AzFramework
@@ -0,0 +1,126 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Events/InputChannelEventListener.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelEventListener::InputChannelEventListener()
: m_filter()
, m_priority(GetPriorityDefault())
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelEventListener::InputChannelEventListener(bool autoConnect)
: m_filter()
, m_priority(GetPriorityDefault())
{
if (autoConnect)
{
Connect();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelEventListener::InputChannelEventListener(AZ::s32 priority)
: m_filter()
, m_priority(priority)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelEventListener::InputChannelEventListener(AZ::s32 priority, bool autoConnect)
: m_filter()
, m_priority(priority)
{
if (autoConnect)
{
Connect();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelEventListener::InputChannelEventListener(AZStd::shared_ptr<InputChannelEventFilter> filter)
: m_filter(filter)
, m_priority(GetPriorityDefault())
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelEventListener::InputChannelEventListener(AZStd::shared_ptr<InputChannelEventFilter> filter,
AZ::s32 priority)
: m_filter(filter)
, m_priority(priority)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelEventListener::InputChannelEventListener(AZStd::shared_ptr<InputChannelEventFilter> filter,
AZ::s32 priority,
bool autoConnect)
: m_filter(filter)
, m_priority(priority)
{
if (autoConnect)
{
Connect();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZ::s32 InputChannelEventListener::GetPriority() const
{
return m_priority;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelEventListener::SetFilter(AZStd::shared_ptr<InputChannelEventFilter> filter)
{
m_filter = filter;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelEventListener::Connect()
{
InputChannelNotificationBus::Handler::BusConnect();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelEventListener::Disconnect()
{
InputChannelNotificationBus::Handler::BusDisconnect();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelEventListener::OnInputChannelEvent(const InputChannel& inputChannel,
bool& o_hasBeenConsumed)
{
if (o_hasBeenConsumed)
{
return;
}
if (m_filter)
{
const bool doesPassFilter = m_filter->DoesPassFilter(inputChannel);
if (!doesPassFilter)
{
return;
}
}
o_hasBeenConsumed = OnInputChannelEventFiltered(inputChannel);
}
} // namespace AzFramework
@@ -0,0 +1,135 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Buses/Notifications/InputChannelNotificationBus.h>
#include <AzFramework/Input/Events/InputChannelEventFilter.h>
#include <AzFramework/Input/Channels/InputChannel.h>
#include <AzFramework/Input/Devices/InputDevice.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
// Start: fix for windows defining max/min macros
#pragma push_macro("max")
#pragma push_macro("min")
#undef max
#undef min
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Class that handles input notifications by priority, and that allows events to be filtered by
//! their channel name, device name, device index (local player) or any combination of the three.
class InputChannelEventListener : public InputChannelNotificationBus::Handler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
///@{
//! Predefined input event listener priority, used to sort handlers from highest to lowest
inline static AZ::s32 GetPriorityFirst() { return std::numeric_limits<AZ::s32>::max(); }
inline static AZ::s32 GetPriorityDebug() { return (GetPriorityFirst() / 4) * 3; }
inline static AZ::s32 GetPriorityUI() { return GetPriorityFirst() / 2; }
inline static AZ::s32 GetPriorityDefault() { return 0; }
inline static AZ::s32 GetPriorityLast() { return std::numeric_limits<AZ::s32>::min(); }
///@}
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
InputChannelEventListener();
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] autoConnect Whether to connect to the input notification bus on construction
explicit InputChannelEventListener(bool autoConnect);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] priority The priority used to sort relative to other input event listeners
explicit InputChannelEventListener(AZ::s32 priority);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] priority The priority used to sort relative to other input event listeners
//! \param[in] autoConnect Whether to connect to the input notification bus on construction
explicit InputChannelEventListener(AZ::s32 priority, bool autoConnect);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] filter The filter used to determine whether an inut event should be handled
explicit InputChannelEventListener(AZStd::shared_ptr<InputChannelEventFilter> filter);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] filter The filter used to determine whether an inut event should be handled
//! \param[in] priority The priority used to sort relative to other input event listeners
explicit InputChannelEventListener(AZStd::shared_ptr<InputChannelEventFilter> filter,
AZ::s32 priority);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] filter The filter used to determine whether an inut event should be handled
//! \param[in] priority The priority used to sort relative to other input event listeners
//! \param[in] autoConnect Whether to connect to the input notification bus on construction
explicit InputChannelEventListener(AZStd::shared_ptr<InputChannelEventFilter> filter,
AZ::s32 priority,
bool autoConnect);
////////////////////////////////////////////////////////////////////////////////////////////
// Default copying
AZ_DEFAULT_COPY(InputChannelEventListener);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
~InputChannelEventListener() override = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelNotifications::GetPriority
AZ::s32 GetPriority() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Allow the filter to be set as necessary even if already connected to the input event bus
//! \param[in] filter The filter used to determine whether an inut event should be handled
void SetFilter(AZStd::shared_ptr<InputChannelEventFilter> filter);
////////////////////////////////////////////////////////////////////////////////////////////
//! Connect to the input notification bus to start receiving input notifications
void Connect();
////////////////////////////////////////////////////////////////////////////////////////////
//! Disconnect from the input notification bus to stop receiving input notifications
void Disconnect();
protected:
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelNotifications::OnInputChannelEvent
void OnInputChannelEvent(const InputChannel& inputChannel, bool& o_hasBeenConsumed) final;
////////////////////////////////////////////////////////////////////////////////////////////
//! Override to be notified when an input channel is active or its state or value is updated,
//! unless the event was consumed by a higher priority listener, or did not pass the filter.
//! \param[in] inputChannel The input channel that is active or whose state or value updated
//! \return True if the input event has been consumed, false otherwise
virtual bool OnInputChannelEventFiltered(const InputChannel& inputChannel) = 0;
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
AZStd::shared_ptr<InputChannelEventFilter> m_filter; //!< The shared input event filter
AZ::s32 m_priority; //!< The priority used for sorting
};
} // namespace AzFramework
// End: fix for windows defining max/min macros
#pragma pop_macro("max")
#pragma pop_macro("min")
@@ -0,0 +1,56 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Events/InputChannelEventSink.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelEventSink::InputChannelEventSink()
: m_filter()
{
InputChannelNotificationBus::Handler::BusConnect();
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelEventSink::~InputChannelEventSink()
{
InputChannelNotificationBus::Handler::BusDisconnect();
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelEventSink::InputChannelEventSink(AZStd::shared_ptr<InputChannelEventFilter> filter)
: m_filter(filter)
{
InputChannelNotificationBus::Handler::BusConnect();
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZ::s32 InputChannelEventSink::GetPriority() const
{
return std::numeric_limits<AZ::s32>::max();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelEventSink::SetFilter(AZStd::shared_ptr<InputChannelEventFilter> filter)
{
m_filter = filter;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputChannelEventSink::OnInputChannelEvent(const InputChannel& inputChannel,
bool& o_hasBeenConsumed)
{
o_hasBeenConsumed = m_filter ? m_filter->DoesPassFilter(inputChannel) : true;
}
} // namespace AzFramework
@@ -0,0 +1,61 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Buses/Notifications/InputChannelNotificationBus.h>
#include <AzFramework/Input/Events/InputChannelEventFilter.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Class that consumes all input event that pass the specified filter.
class InputChannelEventSink : public InputChannelNotificationBus::Handler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
InputChannelEventSink();
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] filter The filter used to determine whether an inut event should be consumed
explicit InputChannelEventSink(AZStd::shared_ptr<InputChannelEventFilter> filter);
////////////////////////////////////////////////////////////////////////////////////////////
// Default copying
AZ_DEFAULT_COPY(InputChannelEventSink);
////////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
~InputChannelEventSink() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelNotifications::GetPriority
AZ::s32 GetPriority() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Allow the filter to be set as necessary even if already connected to the input event bus
//! \param[in] filter The filter used to determine whether an inut event should be consumed
void SetFilter(AZStd::shared_ptr<InputChannelEventFilter> filter);
protected:
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelNotifications::OnInputChannelEvent
void OnInputChannelEvent(const InputChannel& inputChannel, bool& o_hasBeenConsumed) final;
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
AZStd::shared_ptr<InputChannelEventFilter> m_filter; //!< The shared input event filter
};
} // namespace AzFramework
@@ -0,0 +1,76 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Events/InputTextEventListener.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
InputTextEventListener::InputTextEventListener()
: m_priority(GetPriorityDefault())
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputTextEventListener::InputTextEventListener(bool autoConnect)
: m_priority(GetPriorityDefault())
{
if (autoConnect)
{
Connect();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputTextEventListener::InputTextEventListener(AZ::s32 priority)
: m_priority(priority)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputTextEventListener::InputTextEventListener(AZ::s32 priority, bool autoConnect)
: m_priority(priority)
{
if (autoConnect)
{
Connect();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZ::s32 InputTextEventListener::GetPriority() const
{
return m_priority;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputTextEventListener::Connect()
{
InputTextNotificationBus::Handler::BusConnect();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputTextEventListener::Disconnect()
{
InputTextNotificationBus::Handler::BusDisconnect();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputTextEventListener::OnInputTextEvent(const AZStd::string& textUTF8, bool& o_hasBeenConsumed)
{
if (!o_hasBeenConsumed)
{
o_hasBeenConsumed = OnInputTextEventFiltered(textUTF8);
}
}
} // namespace AzFramework
@@ -0,0 +1,102 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Buses/Notifications/InputTextNotificationBus.h>
// Start: fix for windows defining max/min macros
#pragma push_macro("max")
#pragma push_macro("min")
#undef max
#undef min
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Class that handles input text event notifications by priority
class InputTextEventListener : public InputTextNotificationBus::Handler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
///@{
//! Predefined text event listener priority, used to sort handlers from highest to lowest
inline static AZ::s32 GetPriorityFirst() { return std::numeric_limits<AZ::s32>::max(); }
inline static AZ::s32 GetPriorityDebug() { return (GetPriorityFirst() / 4) * 3; }
inline static AZ::s32 GetPriorityUI() { return GetPriorityFirst() / 2; }
inline static AZ::s32 GetPriorityDefault() { return 0; }
inline static AZ::s32 GetPriorityLast() { return std::numeric_limits<AZ::s32>::min(); }
///@}
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
explicit InputTextEventListener();
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] autoConnect Whether to connect to the input notification bus on construction
explicit InputTextEventListener(bool autoConnect);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] priority The priority used to sort relative to other text event listeners
explicit InputTextEventListener(AZ::s32 priority);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] priority The priority used to sort relative to other text event listeners
//! \param[in] autoConnect Whether to connect to the input notification bus on construction
explicit InputTextEventListener(AZ::s32 priority, bool autoConnect);
////////////////////////////////////////////////////////////////////////////////////////////
// Default copying
AZ_DEFAULT_COPY(InputTextEventListener);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
~InputTextEventListener() override = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputTextNotifications::GetPriority
AZ::s32 GetPriority() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Connect to the input notification bus to start receiving input notifications
void Connect();
////////////////////////////////////////////////////////////////////////////////////////////
//! Disconnect from the input notification bus to stop receiving input notifications
void Disconnect();
protected:
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputTextNotifications::OnInputTextEvent
void OnInputTextEvent(const AZStd::string& textUTF8, bool& o_hasBeenConsumed) final;
////////////////////////////////////////////////////////////////////////////////////////////
//! Override to be notified when input text events are generated, but not those already been
//! consumed by a higher priority listener, or those that do not pass this listener's filter.
//! \param[in] textUTF8 The text to process (encoded using UTF-8)
//! \return True if the text event has been consumed, false otherwise
////////////////////////////////////////////////////////////////////////////////////////////
virtual bool OnInputTextEventFiltered(const AZStd::string& textUTF8) = 0;
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
AZ::s32 m_priority; //!< The priority used to sort relative to other input event listeners
};
} // namespace AzFramework
// End: fix for windows defining max/min macros
#pragma pop_macro("max")
#pragma pop_macro("min")
@@ -0,0 +1,41 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Mappings/InputMapping.h>
#include <AzFramework/Input/Contexts/InputContext.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
InputMapping::InputMapping(const InputChannelId& inputChannelId, const InputContext& inputContext)
: InputChannel(inputChannelId, inputContext)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputMapping::ProcessPotentialSourceInputEvent(const InputChannel& inputChannel)
{
return IsSourceInput(inputChannel) ? OnSourceInputEvent(inputChannel) : false;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputMapping::OnTick()
{
// The 'Ended' state for any input channel only lasts for one tick, but we will not receive
// any events when a source input transitions from Ended->Idle, and we need to do that here.
if (IsStateEnded())
{
ResetState();
}
}
} // namespace AzFramework
@@ -0,0 +1,96 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Channels/InputChannel.h>
#include <AzFramework/Input/Devices/InputDevice.h>
#include <AzCore/std/containers/unordered_set.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
class InputContext;
////////////////////////////////////////////////////////////////////////////////////////////////
//! Base class for all input mappings that listen for 'raw' input and output custom input events.
//! Derived classes should provide additional functions that allow their parent input context to
//! update the state and value(s) of the input mapping as raw input is received from the system,
//! and they can (optionally) override the virtual GetCustomData function to return custom data.
class InputMapping : public InputChannel
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputMapping, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputMapping, "{37CA842F-F6E4-47CD-947A-C9B82A2A4DA2}", InputChannel);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputChannelId Id of the input mapping
//! \param[in] inputContext Input context that owns the input mapping
InputMapping(const InputChannelId& inputChannelId,
const InputContext& inputContext);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputMapping);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default Destructor
~InputMapping() override = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Process a potential source input event, that will in turn call OnSourceInputEvent if the
//! provided input channel is a source input for this input mapping.
//! \param[in] inputChannel The input channel that is a potential source input
//! \return True if the input is a source and the event has been consumed, false otherwise
bool ProcessPotentialSourceInputEvent(const InputChannel& inputChannel);
////////////////////////////////////////////////////////////////////////////////////////////
//! Tick/update the input mapping
virtual void OnTick();
protected:
////////////////////////////////////////////////////////////////////////////////////////////
//! Override to return whether an input channel is a source input for this input mapping.
//! \param[in] inputChannel The input channel that is a potential source input
//! \return True if the input channel is a source input, false otherwise
virtual bool IsSourceInput(const InputChannel& inputChannel) = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Override to be notified when a source input is active or its state or value is updated.
//! \param[in] inputChannel The input channel that is active or whose state or value updated
//! \return True if the input event has been consumed, false otherwise
virtual bool OnSourceInputEvent(const InputChannel& inputChannel) = 0;
////////////////////////////////////////////////////////////////////////////////////////////
///@{
//! Alias for verbose container class
using SourceInputChannelIds = AZStd::unordered_set<InputChannelId>;
using ActiveInputChannelIds = AZStd::unordered_map<InputChannelId, InputDeviceId>;
///@}
////////////////////////////////////////////////////////////////////////////////////////////
//! Empty snapshot of an input channel used as the 'default' state for some input mappings.
struct EmptySnapshot : public Snapshot
{
////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
EmptySnapshot() : Snapshot(InputChannelId(), InputDeviceId(""), State::Idle) {}
};
};
} // namespace AzFramework
@@ -0,0 +1,152 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Mappings/InputMappingAnd.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
InputMappingAnd::InputMappingAnd(const InputChannelId& inputChannelId, const InputContext& inputContext)
: InputMapping(inputChannelId, inputContext)
, m_sourceInputChannelIds()
, m_activeInputChannelIds()
, m_averageValue(0.0f)
, m_averageDelta(0.0f)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputMappingAnd::AddSourceInput(const InputChannelId& inputSourceId)
{
const auto it = m_sourceInputChannelIds.find(inputSourceId);
if (it != m_sourceInputChannelIds.end())
{
AZ_Warning("InputMappingAnd", false,
"Input mapping (%s) already contains an input source with id: %s, cannot add",
GetInputChannelId().GetName(), inputSourceId.GetName());
return false;
}
m_sourceInputChannelIds.insert(inputSourceId);
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputMappingAnd::RemoveSourceInput(const InputChannelId& inputSourceId)
{
const auto it = m_sourceInputChannelIds.find(inputSourceId);
if (it == m_sourceInputChannelIds.end())
{
AZ_Warning("InputMappingAnd", false,
"Input mapping (%s) does not contain an input source with id: %s, cannot remove",
GetInputChannelId().GetName(), inputSourceId.GetName());
return false;
}
m_sourceInputChannelIds.erase(it);
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
float InputMappingAnd::GetValue() const
{
return m_averageValue;
}
////////////////////////////////////////////////////////////////////////////////////////////////
float InputMappingAnd::GetDelta() const
{
return m_averageDelta;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputMappingAnd::ResetState()
{
m_averageValue = 0.0f;
m_averageDelta = 0.0f;
m_activeInputChannelIds.clear();
InputChannel::ResetState();
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputMappingAnd::IsSourceInput(const InputChannel& inputChannel)
{
const InputChannelId& inputChannelId = inputChannel.GetInputChannelId();
for (const InputChannelId& sourceInputChannelId : m_sourceInputChannelIds)
{
if (inputChannelId == sourceInputChannelId)
{
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputMappingAnd::OnSourceInputEvent(const InputChannel& inputChannel)
{
// It's possible for multiple device instances of the same type to exist at the same time,
// so we need to store the unique device id (which accounts for the device index) for all
// active sources, and ignore input from any device of the same type but different index.
const InputDeviceId& inputDeviceId = inputChannel.GetInputDevice().GetInputDeviceId();
for (const auto& activeInputChannelId : m_activeInputChannelIds)
{
const InputDeviceId& activeInputDeviceId = activeInputChannelId.second;
if (inputDeviceId.GetNameCrc32() == activeInputDeviceId.GetNameCrc32() &&
inputDeviceId.GetIndex() != activeInputDeviceId.GetIndex())
{
return false;
}
}
// Add or remove the input source from the container tracking active input sources.
if (inputChannel.IsActive())
{
// This entry might already exist in the container, in which case this does nothing.
m_activeInputChannelIds.insert({ inputChannel.GetInputChannelId(), inputDeviceId });
}
else
{
m_activeInputChannelIds.erase(inputChannel.GetInputChannelId());
}
// Determine whether all sources are active, then store the average value and average delta.
const bool isActive = m_activeInputChannelIds.size() == m_sourceInputChannelIds.size();
const float newAverageValue = isActive ? CalculateAverageValue() : 0.0f;
m_averageDelta = newAverageValue - m_averageValue;
m_averageValue = newAverageValue;
// Update the state of this mapping/channel and return.
UpdateState(isActive);
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
float InputMappingAnd::CalculateAverageValue() const
{
float sumOfValues = 0.0f;
for (const auto& activeInputChannelId : m_activeInputChannelIds)
{
const InputChannel* activeInputChannel = InputChannelRequests::FindInputChannel(activeInputChannelId.first,
activeInputChannelId.second.GetIndex());
if (activeInputChannel)
{
sumOfValues += activeInputChannel->GetValue();
}
}
const float numValues = aznumeric_cast<float>(m_activeInputChannelIds.size());
return sumOfValues / numValues;
}
} // namespace AzFramework
@@ -0,0 +1,97 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Mappings/InputMapping.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Class that maps multiple different sources to a single output using 'AND' logic.
//! Example: "gamepad_button_L1" AND "gamepad_button_R1" -> "gameplay_strong_attack"
class InputMappingAnd : public InputMapping
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputMappingAnd, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputMappingAnd, "{9ADDAB97-E786-4BB9-99EE-924EE956E56A}", InputMapping);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputChannelId Id of the input mapping
//! \param[in] inputContext Input context that owns the input mapping
InputMappingAnd(const InputChannelId& inputChannelId,
const InputContext& inputContext);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputMappingAnd);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default Destructor
~InputMappingAnd() override = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Add an input source to this input mapping
//! \param[in] inputSourceId The id of the input source to add to this input mapping
//! \return True if the input source was added to this input mapping, false otherwise
bool AddSourceInput(const InputChannelId& inputSourceId);
////////////////////////////////////////////////////////////////////////////////////////////
//! Remove an input source from this input mapping
//! \param[in] inputSourceId The id of the input source to remove from this input mapping
//! \return True if the input source was removed from this input mapping, false otherwise
bool RemoveSourceInput(const InputChannelId& inputSourceId);
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the average value of the source input channels currently activating this input
//! \return The value of the source input channels currently activating this input mapping
float GetValue() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the average delta of the source input channels currently activating this input
//! \return Difference between the current and last reported values of this input mapping
float GetDelta() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelRequests::ResetState
void ResetState() override;
protected:
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputMapping::IsSourceInput
bool IsSourceInput(const InputChannel& inputChannel) override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputMapping::OnSourceInputEvent
bool OnSourceInputEvent(const InputChannel& inputChannel) override;
private:
////////////////////////////////////////////////////////////////////////////////////////////
//! Calculate the average value of the active source input channels
float CalculateAverageValue() const;
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
SourceInputChannelIds m_sourceInputChannelIds; //!< All source input channel ids
ActiveInputChannelIds m_activeInputChannelIds; //!< Active source input channel/device ids
float m_averageValue = 0.0f; //!< The average value of the active source input channels
float m_averageDelta = 0.0f; //!< The delta between the current and last average value
};
} // namespace AzFramework
@@ -0,0 +1,102 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/Mappings/InputMappingOr.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
InputMappingOr::InputMappingOr(const InputChannelId& inputChannelId, const InputContext& inputContext)
: InputMapping(inputChannelId, inputContext)
, m_sourceInputChannelIds()
, m_currentlyActiveSource(EmptySnapshot())
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputMappingOr::AddSourceInput(const InputChannelId& inputSourceId)
{
const auto it = m_sourceInputChannelIds.find(inputSourceId);
if (it != m_sourceInputChannelIds.end())
{
AZ_Warning("InputMappingOr", false,
"Input mapping (%s) already contains an input source with id: %s, cannot add",
GetInputChannelId().GetName(), inputSourceId.GetName());
return false;
}
m_sourceInputChannelIds.insert(inputSourceId);
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputMappingOr::RemoveSourceInput(const InputChannelId& inputSourceId)
{
const auto it = m_sourceInputChannelIds.find(inputSourceId);
if (it == m_sourceInputChannelIds.end())
{
AZ_Warning("InputMappingOr", false,
"Input mapping (%s) does not contain an input source with id: %s, cannot remove",
GetInputChannelId().GetName(), inputSourceId.GetName());
return false;
}
m_sourceInputChannelIds.erase(it);
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
float InputMappingOr::GetValue() const
{
return m_currentlyActiveSource.m_value;
}
////////////////////////////////////////////////////////////////////////////////////////////////
float InputMappingOr::GetDelta() const
{
return m_currentlyActiveSource.m_delta;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputMappingOr::ResetState()
{
m_currentlyActiveSource = EmptySnapshot();
InputChannel::ResetState();
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputMappingOr::IsSourceInput(const InputChannel& inputChannel)
{
const InputChannelId& inputChannelId = inputChannel.GetInputChannelId();
for (const InputChannelId& sourceInputChannelId : m_sourceInputChannelIds)
{
if (inputChannelId == sourceInputChannelId)
{
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool InputMappingOr::OnSourceInputEvent(const InputChannel& inputChannel)
{
// Capture the source input even when in the 'Ended' state in order to capture the delta.
m_currentlyActiveSource = inputChannel.IsStateIdle() ?
EmptySnapshot() :
InputChannel::Snapshot(inputChannel);
UpdateState(inputChannel.IsActive());
return true;
}
} // namespace AzFramework
@@ -0,0 +1,90 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Mappings/InputMapping.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Class that maps multiple different sources to a single output using 'OR' logic.
//! Example: "gamepad_button_a" OR "keyboard_key_edit_space" -> "gameplay_jump"
class InputMappingOr : public InputMapping
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputMappingOr, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputMappingOr, "{521D2450-2877-4F9F-A320-9989A4F3E781}", InputMapping);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] inputChannelId Id of the input mapping
//! \param[in] inputContext Input context that owns the input mapping
InputMappingOr(const InputChannelId& inputChannelId,
const InputContext& inputContext);
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copying
AZ_DISABLE_COPY_MOVE(InputMappingOr);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default Destructor
~InputMappingOr() override = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Add an input source to this input mapping
//! \param[in] inputSourceId The id of the input source to add to this input mapping
//! \return True if the input source was added to this input mapping, false otherwise
bool AddSourceInput(const InputChannelId& inputSourceId);
////////////////////////////////////////////////////////////////////////////////////////////
//! Remove an input source from this input mapping
//! \param[in] inputSourceId The id of the input source to remove from this input mapping
//! \return True if the input source was removed from this input mapping, false otherwise
bool RemoveSourceInput(const InputChannelId& inputSourceId);
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the value of the source input channel currently activating this input mapping
//! \return The value of the source input channel currently activating this input mapping
float GetValue() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Access to the delta of the source input channel currently activating this input mapping
//! \return Difference between the current and last reported values of this input mapping
float GetDelta() const override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputChannelRequests::ResetState
void ResetState() override;
protected:
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputMapping::IsSourceInput
bool IsSourceInput(const InputChannel& inputChannel) override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputMapping::OnSourceInputEvent
bool OnSourceInputEvent(const InputChannel& inputChannel) override;
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
SourceInputChannelIds m_sourceInputChannelIds; //!< All potential source input channel ids
InputChannel::Snapshot m_currentlyActiveSource; //!< A snapshot of the active source input
};
} // namespace AzFramework
@@ -0,0 +1,297 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzFramework/Input/System/InputSystemComponent.h>
#include <AzFramework/Input/Buses/Notifications/InputSystemNotificationBus.h>
#include <AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Devices/Motion/InputDeviceMotion.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Input/Devices/Touch/InputDeviceTouch.h>
#include <AzFramework/Input/Devices/VirtualKeyboard/InputDeviceVirtualKeyboard.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
AZStd::vector<AZStd::string> GetAllMotionChannelNames()
{
AZStd::vector<AZStd::string> allMotionChannelNames;
for (AZ::u32 i = 0; i < InputDeviceMotion::Acceleration::All.size(); ++i)
{
const InputChannelId& channelId = InputDeviceMotion::Acceleration::All[i];
allMotionChannelNames.push_back(channelId.GetName());
}
for (AZ::u32 i = 0; i < InputDeviceMotion::RotationRate::All.size(); ++i)
{
const InputChannelId& channelId = InputDeviceMotion::RotationRate::All[i];
allMotionChannelNames.push_back(channelId.GetName());
}
for (AZ::u32 i = 0; i < InputDeviceMotion::MagneticField::All.size(); ++i)
{
const InputChannelId& channelId = InputDeviceMotion::MagneticField::All[i];
allMotionChannelNames.push_back(channelId.GetName());
}
for (AZ::u32 i = 0; i < InputDeviceMotion::Orientation::All.size(); ++i)
{
const InputChannelId& channelId = InputDeviceMotion::Orientation::All[i];
allMotionChannelNames.push_back(channelId.GetName());
}
return allMotionChannelNames;
}
////////////////////////////////////////////////////////////////////////////////////////////////
class InputSystemNotificationBusBehaviorHandler
: public InputSystemNotificationBus::Handler
, public AZ::BehaviorEBusHandler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
AZ_EBUS_BEHAVIOR_BINDER(InputSystemNotificationBusBehaviorHandler, "{2F3417A3-41FD-4FBB-B0B6-F154F068F4F8}", AZ::SystemAllocator
, OnPreInputUpdate
, OnPostInputUpdate
);
////////////////////////////////////////////////////////////////////////////////////////////
void OnPreInputUpdate() override
{
Call(FN_OnPreInputUpdate);
}
////////////////////////////////////////////////////////////////////////////////////////////
void OnPostInputUpdate() override
{
Call(FN_OnPostInputUpdate);
}
};
////////////////////////////////////////////////////////////////////////////////////////////////
void InputSystemComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<InputSystemComponent, AZ::Component>()
->Version(1)
->Field("MouseMovementSampleRateHertz", &InputSystemComponent::m_mouseMovementSampleRateHertz)
->Field("GamepadsEnabled", &InputSystemComponent::m_gamepadsEnabled)
->Field("KeyboardEnabled", &InputSystemComponent::m_keyboardEnabled)
->Field("MotionEnabled", &InputSystemComponent::m_motionEnabled)
->Field("MouseEnabled", &InputSystemComponent::m_mouseEnabled)
->Field("TouchEnabled", &InputSystemComponent::m_touchEnabled)
->Field("VirtualKeyboardEnabled", &InputSystemComponent::m_virtualKeyboardEnabled)
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<InputSystemComponent>(
"Input System", "Controls which core input devices are made available")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Engine")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System", 0xc94d118b))
->DataElement(AZ::Edit::UIHandlers::SpinBox, &InputSystemComponent::m_mouseMovementSampleRateHertz,
"Mouse Movement Sample Rate", "The mouse movement sample rate in Hertz (cycles per second), which directly\n"
"correlates to the max number of mouse movement events dispatched each frame.\n"
"Increasing this may improve responsiveness, but could impact performance.\n"
"Decreasing it may improve performance, but could make it less responsive.")
->Attribute(AZ::Edit::Attributes::Min, 1)
->DataElement(AZ::Edit::UIHandlers::SpinBox, &InputSystemComponent::m_gamepadsEnabled,
"Gamepads", "The number of game-pads enabled.")
->Attribute(AZ::Edit::Attributes::Min, 0)
->Attribute(AZ::Edit::Attributes::Max, 8)
->DataElement(AZ::Edit::UIHandlers::CheckBox, &InputSystemComponent::m_keyboardEnabled,
"Keyboard", "Is keyboard input enabled?")
->DataElement(AZ::Edit::UIHandlers::CheckBox, &InputSystemComponent::m_motionEnabled,
"Motion", "Is motion input enabled?")
->DataElement(AZ::Edit::UIHandlers::CheckBox, &InputSystemComponent::m_mouseEnabled,
"Mouse", "Is mouse input enabled?")
->DataElement(AZ::Edit::UIHandlers::CheckBox, &InputSystemComponent::m_touchEnabled,
"Touch", "Is touch enabled?")
->DataElement(AZ::Edit::UIHandlers::CheckBox, &InputSystemComponent::m_virtualKeyboardEnabled,
"Virtual Keyboard", "Is the virtual keyboard enabled?")
;
}
}
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<InputSystemNotificationBus>("InputSystemNotificationBus")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview)
->Attribute(AZ::Script::Attributes::Category, "Input")
->Handler<InputSystemNotificationBusBehaviorHandler>()
;
behaviorContext->EBus<InputSystemRequestBus>("InputSystemRequestBus")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview)
->Attribute(AZ::Script::Attributes::Category, "Input")
->Event("RecreateEnabledInputDevices", &InputSystemRequestBus::Events::RecreateEnabledInputDevices)
;
}
InputChannelId::Reflect(context);
InputDeviceId::Reflect(context);
InputChannel::Reflect(context);
InputDevice::Reflect(context);
LocalUserIdReflect(context);
InputDeviceGamepad::Reflect(context);
InputDeviceKeyboard::Reflect(context);
InputDeviceMotion::Reflect(context);
InputDeviceMouse::Reflect(context);
InputDeviceTouch::Reflect(context);
InputDeviceVirtualKeyboard::Reflect(context);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("InputSystemService", 0x5438d51a));
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("InputSystemService", 0x5438d51a));
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputSystemComponent::InputSystemComponent()
: m_gamepads()
, m_keyboard()
, m_motion()
, m_mouse()
, m_touch()
, m_virtualKeyboard()
, m_mouseMovementSampleRateHertz(InputDeviceMouse::MovementSampleRateDefault)
, m_gamepadsEnabled(4)
, m_keyboardEnabled(true)
, m_motionEnabled(true)
, m_mouseEnabled(true)
, m_touchEnabled(true)
, m_virtualKeyboardEnabled(true)
, m_currentlyUpdatingInputDevices(false)
, m_recreateInputDevicesAfterUpdate(false)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputSystemComponent::~InputSystemComponent()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputSystemComponent::Activate()
{
// Create all enabled input devices
CreateEnabledInputDevices();
InputSystemRequestBus::Handler::BusConnect();
AZ::TickBus::Handler::BusConnect();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputSystemComponent::Deactivate()
{
AZ::TickBus::Handler::BusDisconnect();
InputSystemRequestBus::Handler::BusDisconnect();
// Destroy all enabled input devices
DestroyEnabledInputDevices();
}
////////////////////////////////////////////////////////////////////////////////////////////////
int InputSystemComponent::GetTickOrder()
{
return AZ::ComponentTickBus::TICK_INPUT;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputSystemComponent::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*scriptTimePoint*/)
{
TickInput();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputSystemComponent::TickInput()
{
InputSystemNotificationBus::Broadcast(&InputSystemNotifications::OnPreInputUpdate);
m_currentlyUpdatingInputDevices = true;
InputDeviceRequestBus::Broadcast(&InputDeviceRequests::TickInputDevice);
m_currentlyUpdatingInputDevices = false;
InputSystemNotificationBus::Broadcast(&InputSystemNotifications::OnPostInputUpdate);
if (m_recreateInputDevicesAfterUpdate)
{
CreateEnabledInputDevices();
m_recreateInputDevicesAfterUpdate = false;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputSystemComponent::RecreateEnabledInputDevices()
{
if (m_currentlyUpdatingInputDevices)
{
// Delay the request until we've finished updating to protect against getting called in
// response to an input event, in which case calling CreateEnabledInputDevices here will
// cause a crash (when the stack unwinds back up to the device which dispatced the event
// but was then destroyed). An unlikely (but possible) scenario we must protect against.
m_recreateInputDevicesAfterUpdate = true;
}
else
{
CreateEnabledInputDevices();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputSystemComponent::CreateEnabledInputDevices()
{
const AZ::u32 maxSupportedGamepads = InputDeviceGamepad::GetMaxSupportedGamepads();
m_gamepadsEnabled = AZStd::clamp<AZ::u32>(m_gamepadsEnabled, 0, maxSupportedGamepads);
DestroyEnabledInputDevices();
m_gamepads.resize(m_gamepadsEnabled);
for (AZ::u32 i = 0; i < m_gamepadsEnabled; ++i)
{
m_gamepads[i].reset(aznew InputDeviceGamepad(i));
}
m_keyboard.reset(m_keyboardEnabled ? aznew InputDeviceKeyboard() : nullptr);
m_motion.reset(m_motionEnabled ? aznew InputDeviceMotion() : nullptr);
m_mouse.reset(m_mouseEnabled ? aznew InputDeviceMouse() : nullptr);
m_touch.reset(m_touchEnabled ? aznew InputDeviceTouch() : nullptr);
m_virtualKeyboard.reset(m_virtualKeyboardEnabled ? aznew InputDeviceVirtualKeyboard() : nullptr);
if (m_mouse)
{
m_mouse->SetRawMovementSampleRate(m_mouseMovementSampleRateHertz);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputSystemComponent::DestroyEnabledInputDevices()
{
m_virtualKeyboard.reset(nullptr);
m_touch.reset(nullptr);
m_mouse.reset(nullptr);
m_motion.reset(nullptr);
m_keyboard.reset(nullptr);
m_gamepads.clear();
}
} // namespace AzFramework
@@ -0,0 +1,129 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Buses/Requests/InputSystemRequestBus.h>
#include <AzFramework/Input/Buses/Requests/InputSystemCursorRequestBus.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
class InputDeviceGamepad;
class InputDeviceKeyboard;
class InputDeviceMotion;
class InputDeviceMouse;
class InputDeviceTouch;
class InputDeviceVirtualKeyboard;
////////////////////////////////////////////////////////////////////////////////////////////////
//! This system component manages instances of the default input devices supported by the engine.
//! Other systems/modules/gems/games are free to create additional input device instances of any
//! type; this system component manages devices that are supported "out of the box", which other
//! systems (and most games) will expect to be available for platforms where they are supported.
class InputSystemComponent : public AZ::Component
, public AZ::TickBus::Handler
, public InputSystemRequestBus::Handler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
// AZ::Component Setup
AZ_COMPONENT(InputSystemComponent, "{CAF3A025-FAC9-4537-B99E-0A800A9326DF}")
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AZ::ComponentDescriptor::Reflect
static void Reflect(AZ::ReflectContext* reflection);
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AZ::ComponentDescriptor::GetProvidedServices
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AZ::ComponentDescriptor::GetIncompatibleServices
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
InputSystemComponent();
////////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
~InputSystemComponent() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AZ::Component::Activate
void Activate() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AZ::Component::Deactivate
void Deactivate() override;
protected:
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AZ::TickEvents::GetTickOrder
int GetTickOrder() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AZ::TickEvents::OnTick
void OnTick(float deltaTime, AZ::ScriptTimePoint scriptTimePoint) override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputSystemRequests::TickInput
void TickInput() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputSystemRequests::RecreateEnabledInputDevices
void RecreateEnabledInputDevices() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! Create enabled input devices
void CreateEnabledInputDevices();
////////////////////////////////////////////////////////////////////////////////////////////
//! Destroy enabled input devices
void DestroyEnabledInputDevices();
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Disable copy constructor
InputSystemComponent(const InputSystemComponent&) = delete;
////////////////////////////////////////////////////////////////////////////////////////////
// Input Device Variables
AZStd::vector<AZStd::unique_ptr<InputDeviceGamepad>> m_gamepads; //!< Game-pad devices
AZStd::unique_ptr<InputDeviceKeyboard> m_keyboard; //!< Keyboard device
AZStd::unique_ptr<InputDeviceMotion> m_motion; //!< Motion device
AZStd::unique_ptr<InputDeviceMouse> m_mouse; //!< Mouse device
AZStd::unique_ptr<InputDeviceTouch> m_touch; //!< Touch device
AZStd::unique_ptr<InputDeviceVirtualKeyboard> m_virtualKeyboard; //!< Virtual keyboard device
////////////////////////////////////////////////////////////////////////////////////////////
// Serialized Variables
AZ::u32 m_mouseMovementSampleRateHertz; //!< The mouse movement sample rate in Hertz
AZ::u32 m_gamepadsEnabled; //!< The number of enabled game-pads
bool m_keyboardEnabled; //!< Is the keyboard enabled?
bool m_motionEnabled; //!< Is motion enabled?
bool m_mouseEnabled; //!< Is the mouse enabled?
bool m_touchEnabled; //!< Is touch enabled?
bool m_virtualKeyboardEnabled; //!< Is the virtual keyboard enabled?
////////////////////////////////////////////////////////////////////////////////////////////
// Other Variables
bool m_currentlyUpdatingInputDevices; //!< Are we currently updating input devices?
bool m_recreateInputDevicesAfterUpdate; //!< Should we recreate devices after update?
};
} // namespace AzFramework
@@ -0,0 +1,29 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/User/LocalUserId_Platform.h>
#include <limits>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Constant representing any local user id
static const LocalUserId LocalUserIdAny(std::numeric_limits<AZ::u32>::max());
////////////////////////////////////////////////////////////////////////////////////////////////
//! Constant representing no local user id
static const LocalUserId LocalUserIdNone(std::numeric_limits<AZ::u32>::max() - 1);
} // namespace AzFramework
@@ -0,0 +1,86 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Math/Vector2.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Utility function to adjust and normalize an analog input value so that values within a given
//! dead-zone are set to zero, and values outside ramp up smoothly to a given max absolute value.
//! \param[in] value The analog value to adjust and normalize
//! \param[in] deadZone The dead zone within which the value will be set to zero
//! \param[in] maximumAbsoluteValue The maximum absolute value that the value will be clamped to
//! \return The analog value adjusted and normalized using the supplied dead-zone and max values
inline float AdjustForDeadZoneAndNormalizeAnalogInput(float value,
float deadZone,
float maximumAbsoluteValue)
{
const float absValue = fabsf(value);
if (absValue > maximumAbsoluteValue)
{
// Clamp values that exceed the maximum absolute value
value = AZ::GetClamp(value, -1.0f, 1.0f);
}
else if (absValue > deadZone)
{
// Adjust values outside the dead zone so they ramp smoothly from zero to one
const float valueAdjustedForDeadZone = (value == absValue) ? (value - deadZone) : (value + deadZone);
const float maxAbsValAdjustedForDeadZone = maximumAbsoluteValue - deadZone;
value = valueAdjustedForDeadZone / maxAbsValAdjustedForDeadZone;
}
else
{
// Set values within the dead zone to zero
value = 0.0f;
}
return value;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//! Utility function to adjust and normalize thumb-stick values so that values within the given
//! radial dead-zone are set to zero, and values outside ramp up smoothly to a given max radius.
//! \param[in] valueX The thumb-stick x-axis value to adjust and normalize
//! \param[in] valueY The thumb-stick y-axis value to adjust and normalize
//! \param[in] radialDeadZone The radial dead zone within which the values will be set to zero
//! \param[in] maximumRadiusValue The maximum radius value that will be used to clamp the values
//! \return The thumb-stick values adjusted and normalized using the supplied dead-zone and max
inline AZ::Vector2 AdjustForDeadZoneAndNormalizeThumbStickInput(float valueX,
float valueY,
float radialDeadZone,
float maximumRadiusValue)
{
AZ::Vector2 values(valueX, valueY);
const float length = values.GetLength();
if (length > maximumRadiusValue)
{
// Normalize values that exceed the maximum radius value
values /= length;
}
else if (length > radialDeadZone)
{
// Adjust values outside the dead zone so they ramp smoothly from zero to one
const float lengthAdjustedForDeadZone = length - radialDeadZone;
const float maxRadiusAdjustedForDeadZone = maximumRadiusValue - radialDeadZone;
values *= (lengthAdjustedForDeadZone / maxRadiusAdjustedForDeadZone) / length;
}
else
{
// Set values within the dead zone to zero
values.Set(0.0f);
}
return values;
}
} // namespace AzFramework
@@ -0,0 +1,56 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Devices/Gamepad/InputDeviceGamepad.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Input/Devices/Touch/InputDeviceTouch.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
inline bool IsAnyKeyOrButton(const InputChannel& inputChannel)
{
const InputChannelId& channelId = inputChannel.GetInputChannelId();
const InputDeviceId& deviceId = inputChannel.GetInputDevice().GetInputDeviceId();
if (InputDeviceGamepad::IsGamepadDevice(deviceId))
{
const auto& gamepadButtons = InputDeviceGamepad::Button::All;
const auto& gamepadTriggers = InputDeviceGamepad::Trigger::All;
return AZStd::find(gamepadButtons.cbegin(), gamepadButtons.cend(), channelId) != gamepadButtons.cend() ||
AZStd::find(gamepadTriggers.cbegin(), gamepadTriggers.cend(), channelId) != gamepadTriggers.cend();
}
if (InputDeviceKeyboard::IsKeyboardDevice(deviceId))
{
const auto& keyboardKeys = InputDeviceKeyboard::Key::All;
return AZStd::find(keyboardKeys.cbegin(), keyboardKeys.cend(), channelId) != keyboardKeys.cend();
}
if (InputDeviceMouse::IsMouseDevice(deviceId))
{
const auto& mouseButtons = InputDeviceMouse::Button::All;
return AZStd::find(mouseButtons.cbegin(), mouseButtons.cend(), channelId) != mouseButtons.cend();
}
if (InputDeviceTouch::IsTouchDevice(deviceId))
{
const auto& touches = InputDeviceTouch::Touch::All;
return AZStd::find(touches.cbegin(), touches.cend(), channelId) != touches.cend();
}
return false;
}
} // namespace AzFramework
@@ -0,0 +1,99 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzFramework/Input/Buses/Notifications/InputTextNotificationBus.h>
#include <AzFramework/Input/Channels/InputChannelId.h>
#include <AzCore/Debug/Trace.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/vector.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
//! Function template that processes a generic raw input event queue, given an InputChannelClass
//! that defines a ProcessRawInputEvent function that takes a RawEventType as the only parameter.
//! \param[in] rawEventQueuesById A map (keyed by id) of raw input event queues.
//! \param[in] inputChannelsById A map (keyed by id) of the input channels to potentially update.
template<class InputChannelClass, typename RawEventType>
inline void ProcessRawInputEventQueues(
AZStd::unordered_map<InputChannelId, AZStd::vector<RawEventType>>& rawEventQueuesById,
const AZStd::unordered_map<InputChannelId, InputChannelClass*>& inputChannelsById)
{
auto&& it = rawEventQueuesById.begin();
while (it != rawEventQueuesById.end())
{
const InputChannelId& channelId = it->first;
const auto& channelIt = inputChannelsById.find(channelId);
if (channelIt == inputChannelsById.end() || !channelIt->second)
{
// Unknown channel id, warn but handle gracefully
AZ_Warning("ProcessRawInputEventQueues", false,
"Raw input event queued with unrecognized id: %s", channelId.GetName());
rawEventQueuesById.erase(it++);
continue;
}
InputChannelClass& channel = *(channelIt->second);
AZStd::vector<RawEventType>& rawEventQueue = it->second;
if (!rawEventQueue.empty())
{
// Update the input channel once for each raw event queued since the last frame,
// then clear the event queue so it can receive new events over the next frame.
for (const RawEventType& rawEvent : rawEventQueue)
{
channel.ProcessRawInputEvent(rawEvent);
}
rawEventQueue.clear();
}
else
{
// No raw input was received for this channel since the last frame, but we must
// still update it to trigger state transitions and ensure that events are sent.
// If this channel entered the 'Ended' state last frame (and it has not received
// new raw input this frame) this update will cause it to enter the 'Idle' state.
channel.UpdateState(channel.IsActive());
}
if (channel.IsStateIdle())
{
// When a channel returns to the idle state, removing its corresponding event queue
// map entry conveniently allows the map to double as the set of non-idle channels.
// This allows us to continue updating all non-idle channels (above) without having
// to iterate over every channel every frame, as the majority of them will be idle.
rawEventQueuesById.erase(it++);
}
else
{
++it;
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
//! Utility function that processes a queue of raw input text events.
//! \param[in] rawTextEventQueue The queue of raw text events (all encoded using UTF-8)
inline void ProcessRawInputTextEventQueue(AZStd::vector<AZStd::string>& rawTextEventQueue)
{
for (const AZStd::string& rawTextEvent : rawTextEventQueue)
{
bool hasBeenConsumed = false;
InputTextNotificationBus::Broadcast(&InputTextNotifications::OnInputTextEvent,
rawTextEvent,
hasBeenConsumed);
}
rawTextEventQueue.clear();
}
} // namespace AzFramework