90e45a5bbd
Synergy (now Barrier) input was lost when we removed the CryLegacy Gem. This resurrects the code, updates it to work with Barrier (which is the open source project based on the original Synergy core), and updates it to be more conformant with the AzFramework Input framework. Notes: - The majority of code in BarrierInputClient.cpp is still largely unmodified from the original Cry drop and could use some love to make it more robust, but it works so at least gives us somewhere to start. - The current iteration replaces the host platform's default mouse/keyboard implementation upon creation of the BarrierInputClient, and if the connection fails or is later lost we don't restore the default implementations which we should for a better use experience. On a related note, for some use cases it would be better to create additional mouse/keyboard input devices (that use the Barrier implementations) in addition to the existing devices (that use the host platform's default implementation), however this would mean that any system assuming only one mouse/keyboard device exists would not work with the additional Barrier input devices. We can iterate on both of the above issues in the future as needed, perhaps providing configurable options to control the behaviour. Signed-off-by: bosnichd <bosnichd@amazon.com>
196 lines
9.3 KiB
C++
196 lines
9.3 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
#include <BarrierInputMouse.h>
|
|
|
|
#include <Atom/RPI.Public/ViewportContext.h>
|
|
#include <Atom/RPI.Public/ViewportContextBus.h>
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
namespace BarrierInput
|
|
{
|
|
using namespace AzFramework;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
InputDeviceMouse::Implementation* InputDeviceMouseBarrier::Create(InputDeviceMouse& inputDevice)
|
|
{
|
|
return aznew InputDeviceMouseBarrier(inputDevice);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
InputDeviceMouseBarrier::InputDeviceMouseBarrier(InputDeviceMouse& inputDevice)
|
|
: InputDeviceMouse::Implementation(inputDevice)
|
|
, m_systemCursorState(SystemCursorState::Unknown)
|
|
, m_systemCursorPositionNormalized(0.5f, 0.5f)
|
|
, m_threadAwareRawButtonEventQueuesById()
|
|
, m_threadAwareRawButtonEventQueuesByIdMutex()
|
|
, m_threadAwareRawMovementEventQueuesById()
|
|
, m_threadAwareRawMovementEventQueuesByIdMutex()
|
|
, m_threadAwareSystemCursorPosition(0.0f, 0.0f)
|
|
, m_threadAwareSystemCursorPositionMutex()
|
|
{
|
|
RawInputNotificationBusBarrier::Handler::BusConnect();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
InputDeviceMouseBarrier::~InputDeviceMouseBarrier()
|
|
{
|
|
RawInputNotificationBusBarrier::Handler::BusDisconnect();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool InputDeviceMouseBarrier::IsConnected() const
|
|
{
|
|
// We could check the validity of the socket connection to the Barrier server
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void InputDeviceMouseBarrier::SetSystemCursorState(SystemCursorState systemCursorState)
|
|
{
|
|
// This doesn't apply when using Barrier, but we'll store it so it can be queried
|
|
m_systemCursorState = systemCursorState;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
SystemCursorState InputDeviceMouseBarrier::GetSystemCursorState() const
|
|
{
|
|
return m_systemCursorState;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void InputDeviceMouseBarrier::SetSystemCursorPositionNormalized(AZ::Vector2 positionNormalized)
|
|
{
|
|
// This will simply get overridden by the next call to OnRawMousePositionEvent, but there's
|
|
// not much we can do about it, and Barrier mouse input is only for debug purposes anyway.
|
|
m_systemCursorPositionNormalized = positionNormalized;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
AZ::Vector2 InputDeviceMouseBarrier::GetSystemCursorPositionNormalized() const
|
|
{
|
|
return m_systemCursorPositionNormalized;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void InputDeviceMouseBarrier::TickInputDevice()
|
|
{
|
|
{
|
|
// Queue all mouse button events that were received in the other thread
|
|
AZStd::scoped_lock lock(m_threadAwareRawButtonEventQueuesByIdMutex);
|
|
for (const auto& buttonEventQueuesById : m_threadAwareRawButtonEventQueuesById)
|
|
{
|
|
const InputChannelId& inputChannelId = buttonEventQueuesById.first;
|
|
for (bool rawButtonState : buttonEventQueuesById.second)
|
|
{
|
|
QueueRawButtonEvent(inputChannelId, rawButtonState);
|
|
}
|
|
}
|
|
m_threadAwareRawButtonEventQueuesById.clear();
|
|
}
|
|
|
|
bool receivedRawMovementEvents = false;
|
|
{
|
|
// Queue all mouse movement events that were received in the other thread
|
|
AZStd::scoped_lock lock(m_threadAwareRawMovementEventQueuesByIdMutex);
|
|
for (const auto& movementEventQueuesById : m_threadAwareRawMovementEventQueuesById)
|
|
{
|
|
const InputChannelId& inputChannelId = movementEventQueuesById.first;
|
|
for (float rawMovementDelta : movementEventQueuesById.second)
|
|
{
|
|
QueueRawMovementEvent(inputChannelId, rawMovementDelta);
|
|
receivedRawMovementEvents = true;
|
|
}
|
|
}
|
|
m_threadAwareRawMovementEventQueuesById.clear();
|
|
}
|
|
|
|
// Update the system cursor position
|
|
auto atomViewportRequests = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get();
|
|
AZ::RPI::ViewportContextPtr viewportContext = atomViewportRequests->GetDefaultViewportContext();
|
|
if (viewportContext)
|
|
{
|
|
const AzFramework::WindowSize windowSize = viewportContext->GetViewportSize();
|
|
const float windowWidth = static_cast<float>(windowSize.m_width);
|
|
const float windowHeight = static_cast<float>(windowSize.m_height);
|
|
const AZ::Vector2 oldSystemCursorPositionNormalized = m_systemCursorPositionNormalized;
|
|
|
|
AZStd::scoped_lock lock(m_threadAwareSystemCursorPositionMutex);
|
|
{
|
|
const AZ::Vector2 normalizedPosition(m_threadAwareSystemCursorPosition.GetX() / windowWidth,
|
|
m_threadAwareSystemCursorPosition.GetY() / windowHeight);
|
|
m_systemCursorPositionNormalized = normalizedPosition;
|
|
}
|
|
|
|
// In theory Barrier should send relative mouse movement events as 'DMRM' messages, which are
|
|
// forwarded to InputDeviceMouseBarrier::OnRawMouseMovementEvent, but this does not appear to
|
|
// be happening, so if we didn't receive any relative mouse movement events this frame we can
|
|
// just approximate the movement ourselves. Unlike other mouse implementations where movement
|
|
// events are sent 'raw' before any operating system ballistics/smoothing is applied, Barrier
|
|
// seems to calculate relative mouse movement events by taking the delta between the previous
|
|
// system cursor position and the current one, so we should obtain the same result regardless.
|
|
if (!receivedRawMovementEvents)
|
|
{
|
|
const AZ::Vector2 mouseMovementDelta = m_systemCursorPositionNormalized - oldSystemCursorPositionNormalized;
|
|
QueueRawMovementEvent(InputDeviceMouse::Movement::X, mouseMovementDelta.GetX() * windowWidth);
|
|
QueueRawMovementEvent(InputDeviceMouse::Movement::Y, mouseMovementDelta.GetY() * windowHeight);
|
|
}
|
|
}
|
|
|
|
// Process raw event queues once each frame
|
|
ProcessRawEventQueues();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void InputDeviceMouseBarrier::OnRawMouseButtonDownEvent(uint32_t buttonIndex)
|
|
{
|
|
ThreadSafeQueueRawButtonEvent(buttonIndex, true);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void InputDeviceMouseBarrier::OnRawMouseButtonUpEvent(uint32_t buttonIndex)
|
|
{
|
|
ThreadSafeQueueRawButtonEvent(buttonIndex, false);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void InputDeviceMouseBarrier::OnRawMouseMovementEvent(float movementX, float movementY)
|
|
{
|
|
AZStd::scoped_lock lock(m_threadAwareRawMovementEventQueuesByIdMutex);
|
|
m_threadAwareRawMovementEventQueuesById[InputDeviceMouse::Movement::X].push_back(movementX);
|
|
m_threadAwareRawMovementEventQueuesById[InputDeviceMouse::Movement::Y].push_back(movementY);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void InputDeviceMouseBarrier::OnRawMousePositionEvent(float positionX,
|
|
float positionY)
|
|
{
|
|
AZStd::scoped_lock lock(m_threadAwareSystemCursorPositionMutex);
|
|
m_threadAwareSystemCursorPosition = AZ::Vector2(positionX, positionY);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
void InputDeviceMouseBarrier::ThreadSafeQueueRawButtonEvent(uint32_t buttonIndex,
|
|
bool rawButtonState)
|
|
{
|
|
const InputChannelId* inputChannelId = nullptr;
|
|
switch (buttonIndex)
|
|
{
|
|
case 1: { inputChannelId = &InputDeviceMouse::Button::Left; } break;
|
|
case 2: { inputChannelId = &InputDeviceMouse::Button::Middle; } break;
|
|
case 3: { inputChannelId = &InputDeviceMouse::Button::Right; } break;
|
|
}
|
|
|
|
if (inputChannelId)
|
|
{
|
|
AZStd::scoped_lock lock(m_threadAwareRawButtonEventQueuesByIdMutex);
|
|
m_threadAwareRawButtonEventQueuesById[*inputChannelId].push_back(rawButtonState);
|
|
}
|
|
}
|
|
} // namespace BarrierInput
|