mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-11-13 07:17:28 +00:00
[client/idd] network: add recovery, cursor, and control
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -20,8 +20,46 @@
|
||||
|
||||
#include "transport/network/CNetworkSession.h"
|
||||
|
||||
#include <new>
|
||||
#include <utility>
|
||||
|
||||
namespace
|
||||
{
|
||||
uint64_t NextRevision(uint64_t revision)
|
||||
{
|
||||
++revision;
|
||||
if (!revision)
|
||||
++revision;
|
||||
return revision;
|
||||
}
|
||||
|
||||
LGNetControlStatusCode ControlStatusCode(InteractionResult result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case InteractionResult::ACCEPTED:
|
||||
return LG_NET_CONTROL_STATUS_APPLIED;
|
||||
|
||||
case InteractionResult::BUSY:
|
||||
return LG_NET_CONTROL_STATUS_BUSY;
|
||||
|
||||
case InteractionResult::UNAVAILABLE:
|
||||
return LG_NET_CONTROL_STATUS_UNSUPPORTED;
|
||||
|
||||
case InteractionResult::STALE:
|
||||
return LG_NET_CONTROL_STATUS_STALE;
|
||||
|
||||
case InteractionResult::REJECTED:
|
||||
return LG_NET_CONTROL_STATUS_INVALID;
|
||||
|
||||
case InteractionResult::FAILED:
|
||||
return LG_NET_CONTROL_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return LG_NET_CONTROL_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
bool CNetworkEventQueue::Push(const NetworkEvent& event)
|
||||
{
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
@@ -147,47 +185,87 @@ ControlResult CNetworkControlSink::SendCursor(
|
||||
{
|
||||
const bool shape = info.CursorShapeInfo.CursorType !=
|
||||
IDDCX_CURSOR_SHAPE_TYPE_UNINITIALIZED;
|
||||
if (shape && info.CursorShapeInfo.CursorType !=
|
||||
IDDCX_CURSOR_SHAPE_TYPE_ALPHA &&
|
||||
info.CursorShapeInfo.CursorType !=
|
||||
IDDCX_CURSOR_SHAPE_TYPE_MASKED_COLOR)
|
||||
return ControlResult::FAILED;
|
||||
if (shape && info.CursorShapeInfo.Height &&
|
||||
info.CursorShapeInfo.Pitch >
|
||||
SIZE_MAX / info.CursorShapeInfo.Height)
|
||||
return ControlResult::FAILED;
|
||||
const size_t shapeSize = shape ? static_cast<size_t>(
|
||||
info.CursorShapeInfo.Height) * info.CursorShapeInfo.Pitch : 0;
|
||||
if (size != shapeSize || (size && !data))
|
||||
if (size != shapeSize || (size && !data) ||
|
||||
shapeSize > LG_NET_CURSOR_MAX_SHAPE_LENGTH ||
|
||||
(shape && (!info.CursorShapeInfo.Width ||
|
||||
info.CursorShapeInfo.Width > LG_NET_CURSOR_MAX_WIDTH ||
|
||||
!info.CursorShapeInfo.Height ||
|
||||
info.CursorShapeInfo.Height > LG_NET_CURSOR_MAX_HEIGHT ||
|
||||
info.CursorShapeInfo.XHot >= info.CursorShapeInfo.Width ||
|
||||
info.CursorShapeInfo.YHot >= info.CursorShapeInfo.Height ||
|
||||
info.CursorShapeInfo.Pitch < info.CursorShapeInfo.Width * 4U)))
|
||||
return ControlResult::FAILED;
|
||||
|
||||
std::vector<BYTE> cursorData;
|
||||
if (shapeSize)
|
||||
cursorData.assign(data, data + size);
|
||||
try
|
||||
{
|
||||
if (shapeSize)
|
||||
cursorData.assign(data, data + size);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return ControlResult::FAILED;
|
||||
}
|
||||
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
if (!m_events)
|
||||
return ControlResult::RETRY;
|
||||
const UINT whiteLevel = sdrWhiteLevel ?
|
||||
sdrWhiteLevel : LG_SDR_WHITE_LEVEL_DEFAULT;
|
||||
if (m_state.sdrWhiteLevel != whiteLevel)
|
||||
{
|
||||
m_state.sdrWhiteLevel = whiteLevel;
|
||||
m_state.colorRevision = NextRevision(m_state.colorRevision);
|
||||
}
|
||||
m_state.cursor.IsCursorVisible = info.IsCursorVisible;
|
||||
m_state.cursor.X = info.X;
|
||||
m_state.cursor.Y = info.Y;
|
||||
m_state.sdrWhiteLevel = sdrWhiteLevel;
|
||||
m_state.cursorValid = true;
|
||||
m_state.positionRevision = NextRevision(m_state.positionRevision);
|
||||
if (shape)
|
||||
{
|
||||
m_state.cursor.IsCursorShapeUpdated = info.IsCursorShapeUpdated;
|
||||
m_state.cursor.CursorShapeInfo = info.CursorShapeInfo;
|
||||
m_state.cursorData = std::move(cursorData);
|
||||
m_state.shapeValid = true;
|
||||
m_state.shapeRevision = NextRevision(m_state.shapeRevision);
|
||||
}
|
||||
++m_state.revision;
|
||||
m_state.stateRevision = NextRevision(m_state.stateRevision);
|
||||
return ControlResult::APPLIED;
|
||||
}
|
||||
|
||||
ControlResult CNetworkControlSink::SetColorTransform(
|
||||
std::shared_ptr<const D12ColorTransform> transform)
|
||||
{
|
||||
std::shared_ptr<const D12ColorTransform> copy;
|
||||
try
|
||||
{
|
||||
if (transform)
|
||||
copy = std::make_shared<D12ColorTransform>(*transform);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return ControlResult::FAILED;
|
||||
}
|
||||
|
||||
CSRWExclusiveLock lock(m_lock);
|
||||
if (!m_events)
|
||||
return ControlResult::RETRY;
|
||||
m_state.transform = std::move(transform);
|
||||
m_state.transform = std::move(copy);
|
||||
m_state.transformValid = true;
|
||||
++m_state.revision;
|
||||
m_state.colorRevision = NextRevision(m_state.colorRevision);
|
||||
m_state.stateRevision = NextRevision(m_state.stateRevision);
|
||||
return ControlResult::APPLIED;
|
||||
}
|
||||
|
||||
@@ -198,12 +276,21 @@ void CNetworkControlSink::RequestReplay()
|
||||
m_events->OnControlReplay(m_token);
|
||||
}
|
||||
|
||||
bool CNetworkControlSink::Snapshot(NetworkControlSnapshot& state) const
|
||||
bool CNetworkControlSink::Snapshot(
|
||||
NetworkControlSnapshot& state, uint64_t knownRevision) const
|
||||
{
|
||||
CSRWSharedLock lock(m_lock);
|
||||
if (!m_events)
|
||||
if (!m_events || !m_state.stateRevision ||
|
||||
m_state.stateRevision == knownRevision)
|
||||
return false;
|
||||
state = m_state;
|
||||
try
|
||||
{
|
||||
state = m_state;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -243,31 +330,50 @@ void CNetworkSession::Close()
|
||||
m_input.Release(m_source, true);
|
||||
}
|
||||
|
||||
bool CNetworkSession::SetCursorPosition(int32_t x, int32_t y)
|
||||
bool CNetworkSession::SetCursorPosition(
|
||||
uint64_t controlID, int32_t x, int32_t y)
|
||||
{
|
||||
CSRWSharedLock lock(m_lifecycleLock);
|
||||
if (!m_open)
|
||||
if (!m_open || !controlID)
|
||||
return false;
|
||||
|
||||
NetworkEvent event;
|
||||
event.type = NetworkEventType::SET_CURSOR_POSITION;
|
||||
event.source = EventSource();
|
||||
event.x = x;
|
||||
event.y = y;
|
||||
event.type = NetworkEventType::SET_CURSOR_POSITION;
|
||||
event.source = EventSource();
|
||||
event.controlID = controlID;
|
||||
event.x = x;
|
||||
event.y = y;
|
||||
return m_events.Push(event);
|
||||
}
|
||||
|
||||
bool CNetworkSession::SetResolution(uint32_t width, uint32_t height)
|
||||
bool CNetworkSession::SetResolution(
|
||||
uint64_t controlID, uint32_t width, uint32_t height)
|
||||
{
|
||||
CSRWSharedLock lock(m_lifecycleLock);
|
||||
if (!m_open || !width || !height)
|
||||
if (!m_open || !controlID || !width || !height)
|
||||
return false;
|
||||
|
||||
NetworkEvent event;
|
||||
event.type = NetworkEventType::SET_RESOLUTION;
|
||||
event.source = EventSource();
|
||||
event.width = width;
|
||||
event.height = height;
|
||||
event.type = NetworkEventType::SET_RESOLUTION;
|
||||
event.source = EventSource();
|
||||
event.controlID = controlID;
|
||||
event.width = width;
|
||||
event.height = height;
|
||||
return m_events.Push(event);
|
||||
}
|
||||
|
||||
bool CNetworkSession::SetFrameSchedule(
|
||||
uint64_t controlID, const FrameScheduleUpdate& schedule)
|
||||
{
|
||||
CSRWSharedLock lock(m_lifecycleLock);
|
||||
if (!m_open || !controlID)
|
||||
return false;
|
||||
|
||||
NetworkEvent event;
|
||||
event.type = NetworkEventType::SET_FRAME_SCHEDULE;
|
||||
event.source = EventSource();
|
||||
event.schedule = schedule;
|
||||
event.controlID = controlID;
|
||||
return m_events.Push(event);
|
||||
}
|
||||
|
||||
@@ -298,12 +404,33 @@ void CNetworkSession::ProcessEvent(
|
||||
switch (event.type)
|
||||
{
|
||||
case NetworkEventType::SET_CURSOR_POSITION:
|
||||
events.OnSetCursorPos(event.source, event.x, event.y);
|
||||
{
|
||||
NetworkControlStatus status;
|
||||
status.controlID = event.controlID;
|
||||
status.status = ControlStatusCode(
|
||||
events.OnSetCursorPos(event.source, event.x, event.y));
|
||||
SetControlStatus(status);
|
||||
break;
|
||||
}
|
||||
|
||||
case NetworkEventType::SET_RESOLUTION:
|
||||
events.OnSetResolution(event.source, event.width, event.height);
|
||||
{
|
||||
NetworkControlStatus status;
|
||||
status.controlID = event.controlID;
|
||||
status.status = ControlStatusCode(events.OnSetResolution(
|
||||
event.source, event.width, event.height));
|
||||
SetControlStatus(status);
|
||||
break;
|
||||
}
|
||||
|
||||
case NetworkEventType::SET_FRAME_SCHEDULE:
|
||||
{
|
||||
NetworkControlStatus status;
|
||||
status.controlID = event.controlID;
|
||||
status.status = LG_NET_CONTROL_STATUS_UNSUPPORTED;
|
||||
SetControlStatus(status);
|
||||
break;
|
||||
}
|
||||
|
||||
case NetworkEventType::RECOVERY_REQUEST:
|
||||
{
|
||||
@@ -356,6 +483,36 @@ bool CNetworkSession::TakeRecoveryStatus(NetworkRecoveryStatus& status)
|
||||
return true;
|
||||
}
|
||||
|
||||
void CNetworkSession::SetControlStatus(
|
||||
const NetworkControlStatus& status)
|
||||
{
|
||||
CSRWExclusiveLock lock(m_controlLock);
|
||||
if (m_controlCount == CONTROL_CAPACITY)
|
||||
{
|
||||
m_control[m_controlHead] = {};
|
||||
m_controlHead = (m_controlHead + 1) % CONTROL_CAPACITY;
|
||||
--m_controlCount;
|
||||
}
|
||||
|
||||
const unsigned tail =
|
||||
(m_controlHead + m_controlCount) % CONTROL_CAPACITY;
|
||||
m_control[tail] = status;
|
||||
++m_controlCount;
|
||||
}
|
||||
|
||||
bool CNetworkSession::TakeControlStatus(NetworkControlStatus& status)
|
||||
{
|
||||
CSRWExclusiveLock lock(m_controlLock);
|
||||
if (!m_controlCount)
|
||||
return false;
|
||||
|
||||
status = m_control[m_controlHead];
|
||||
m_control[m_controlHead] = {};
|
||||
m_controlHead = (m_controlHead + 1) % CONTROL_CAPACITY;
|
||||
--m_controlCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
InputTargetState CNetworkSession::InputState() const
|
||||
{
|
||||
CSRWSharedLock lock(m_lifecycleLock);
|
||||
|
||||
@@ -21,10 +21,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "CSRWLock.h"
|
||||
#include "capture/CFrameScheduler.h"
|
||||
#include "transport/IControlSink.h"
|
||||
#include "transport/IInputSource.h"
|
||||
#include "transport/ITransport.h"
|
||||
|
||||
#include <LGProtocol/NetworkServices.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
@@ -32,20 +35,23 @@ enum class NetworkEventType : uint8_t
|
||||
{
|
||||
SET_CURSOR_POSITION,
|
||||
SET_RESOLUTION,
|
||||
SET_FRAME_SCHEDULE,
|
||||
RECOVERY_REQUEST,
|
||||
};
|
||||
|
||||
struct NetworkEvent
|
||||
{
|
||||
NetworkEventType type = NetworkEventType::SET_CURSOR_POSITION;
|
||||
SourceKey source;
|
||||
int32_t x = 0;
|
||||
int32_t y = 0;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
uint64_t session = 0;
|
||||
uint32_t serial = 0;
|
||||
bool active = false;
|
||||
NetworkEventType type = NetworkEventType::SET_CURSOR_POSITION;
|
||||
SourceKey source;
|
||||
FrameScheduleUpdate schedule = {};
|
||||
uint64_t controlID = 0;
|
||||
int32_t x = 0;
|
||||
int32_t y = 0;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
uint64_t session = 0;
|
||||
uint32_t serial = 0;
|
||||
bool active = false;
|
||||
};
|
||||
|
||||
struct NetworkRecoveryStatus
|
||||
@@ -57,13 +63,20 @@ struct NetworkRecoveryStatus
|
||||
uint32_t error = 0;
|
||||
};
|
||||
|
||||
struct NetworkControlStatus
|
||||
{
|
||||
uint64_t controlID = 0;
|
||||
LGNetControlStatusCode status = LG_NET_CONTROL_STATUS_ERROR;
|
||||
uint32_t detail = 0;
|
||||
};
|
||||
|
||||
class CNetworkEventQueue
|
||||
{
|
||||
private:
|
||||
static constexpr unsigned CAPACITY = 128;
|
||||
|
||||
CSRWLock m_lock;
|
||||
NetworkEvent m_events[CAPACITY] = {};
|
||||
NetworkEvent m_events[CAPACITY] = {};
|
||||
unsigned m_head = 0;
|
||||
unsigned m_count = 0;
|
||||
|
||||
@@ -101,11 +114,14 @@ struct NetworkControlSnapshot
|
||||
IDARG_OUT_QUERY_HWCURSOR cursor = {};
|
||||
std::vector<BYTE> cursorData;
|
||||
std::shared_ptr<const D12ColorTransform> transform;
|
||||
UINT sdrWhiteLevel = 0;
|
||||
uint64_t revision = 0;
|
||||
bool cursorValid = false;
|
||||
bool shapeValid = false;
|
||||
bool transformValid = false;
|
||||
UINT sdrWhiteLevel = 0;
|
||||
uint64_t stateRevision = 0;
|
||||
uint64_t positionRevision = 0;
|
||||
uint64_t shapeRevision = 0;
|
||||
uint64_t colorRevision = 0;
|
||||
bool cursorValid = false;
|
||||
bool shapeValid = false;
|
||||
bool transformValid = false;
|
||||
};
|
||||
|
||||
class CNetworkControlSink final : public IControlSink
|
||||
@@ -125,7 +141,8 @@ public:
|
||||
std::shared_ptr<const D12ColorTransform> transform) override;
|
||||
|
||||
void RequestReplay();
|
||||
bool Snapshot(NetworkControlSnapshot& state) const;
|
||||
bool Snapshot(
|
||||
NetworkControlSnapshot& state, uint64_t knownRevision) const;
|
||||
};
|
||||
|
||||
// A session is transport-independent state owned by the eventual QUIC
|
||||
@@ -135,6 +152,7 @@ class CNetworkSession final
|
||||
{
|
||||
private:
|
||||
static constexpr unsigned RECOVERY_CAPACITY = 16;
|
||||
static constexpr unsigned CONTROL_CAPACITY = 128;
|
||||
|
||||
CNetworkEventQueue& m_events;
|
||||
CNetworkInputSource& m_input;
|
||||
@@ -143,10 +161,15 @@ private:
|
||||
bool m_open = true;
|
||||
|
||||
CSRWLock m_recoveryLock;
|
||||
NetworkRecoveryStatus m_recovery[RECOVERY_CAPACITY] = {};
|
||||
NetworkRecoveryStatus m_recovery[RECOVERY_CAPACITY] = {};
|
||||
unsigned m_recoveryHead = 0;
|
||||
unsigned m_recoveryCount = 0;
|
||||
|
||||
CSRWLock m_controlLock;
|
||||
NetworkControlStatus m_control[CONTROL_CAPACITY] = {};
|
||||
unsigned m_controlHead = 0;
|
||||
unsigned m_controlCount = 0;
|
||||
|
||||
SourceKey EventSource() const;
|
||||
|
||||
public:
|
||||
@@ -161,12 +184,17 @@ public:
|
||||
bool IsOpen() const;
|
||||
void Close();
|
||||
|
||||
bool SetCursorPosition(int32_t x, int32_t y);
|
||||
bool SetResolution(uint32_t width, uint32_t height);
|
||||
bool SetCursorPosition(uint64_t controlID, int32_t x, int32_t y);
|
||||
bool SetResolution(
|
||||
uint64_t controlID, uint32_t width, uint32_t height);
|
||||
bool SetFrameSchedule(
|
||||
uint64_t controlID, const FrameScheduleUpdate& schedule);
|
||||
bool RequestRecovery(uint64_t session, uint32_t serial, bool active);
|
||||
void ProcessEvent(const NetworkEvent& event, ITransportEvents& events);
|
||||
void SetRecoveryStatus(const NetworkRecoveryStatus& status);
|
||||
bool TakeRecoveryStatus(NetworkRecoveryStatus& status);
|
||||
void SetControlStatus(const NetworkControlStatus& status);
|
||||
bool TakeControlStatus(NetworkControlStatus& status);
|
||||
|
||||
InputTargetState InputState() const;
|
||||
InputResult ClaimInput();
|
||||
|
||||
@@ -241,6 +241,7 @@ bool CNetworkTransport::ServiceEnabled(LGNetService service) const
|
||||
{
|
||||
case LG_NET_SERVICE_RECOVERY:
|
||||
case LG_NET_SERVICE_CURSOR:
|
||||
case LG_NET_SERVICE_CONTROL:
|
||||
return (m_config.services & TRANSPORT_SERVICE_CONTROL) != 0;
|
||||
|
||||
case LG_NET_SERVICE_INPUT:
|
||||
@@ -265,9 +266,9 @@ bool CNetworkTransport::ServiceEnabled(LGNetService service) const
|
||||
}
|
||||
|
||||
bool CNetworkTransport::ControlSnapshot(
|
||||
NetworkControlSnapshot& state) const
|
||||
NetworkControlSnapshot& state, uint64_t knownRevision) const
|
||||
{
|
||||
return m_control.Snapshot(state);
|
||||
return m_control.Snapshot(state, knownRevision);
|
||||
}
|
||||
|
||||
std::shared_ptr<CNetworkSession> CNetworkTransport::CreateSession(
|
||||
|
||||
@@ -94,7 +94,8 @@ public:
|
||||
|
||||
const NetworkSettings& Settings() const { return m_settings; }
|
||||
bool ServiceEnabled(LGNetService service) const;
|
||||
bool ControlSnapshot(NetworkControlSnapshot& state) const;
|
||||
bool ControlSnapshot(
|
||||
NetworkControlSnapshot& state, uint64_t knownRevision) const;
|
||||
|
||||
// The QUIC provider calls these only after authenticating a peer. Keeping
|
||||
// the registry here makes Stop a barrier for session-originated callbacks.
|
||||
|
||||
Reference in New Issue
Block a user