mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 07:01:30 +00:00
[idd] transport: isolate interactive services
This commit is contained in:
@@ -98,6 +98,7 @@ bool CLGMPControl::Initialize()
|
||||
|
||||
void CLGMPControl::DeInit()
|
||||
{
|
||||
SetControlEvents(nullptr, {});
|
||||
for (int i = 0; i < LGMP_Q_POINTER_LEN; ++i)
|
||||
lgmpHostMemFree(&m_pointerMemory[i]);
|
||||
for (int i = 0; i < POINTER_SHAPE_BUFFERS; ++i)
|
||||
@@ -106,7 +107,6 @@ void CLGMPControl::DeInit()
|
||||
lgmpHostMemFree(&m_pointerTransformMemory[i]);
|
||||
|
||||
m_pointerQueue = nullptr;
|
||||
m_pointerShape = nullptr;
|
||||
m_pointerMemoryIndex = 0;
|
||||
m_pointerShapeIndex = 0;
|
||||
m_pointerTransformIndex = 0;
|
||||
@@ -129,43 +129,101 @@ bool CLGMPControl::HasNewSubscribers()
|
||||
return lgmpHostQueueNewSubs(m_pointerQueue) != 0;
|
||||
}
|
||||
|
||||
void CLGMPControl::SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info,
|
||||
const BYTE * data, UINT sdrWhiteLevel)
|
||||
void CLGMPControl::SetControlEvents(
|
||||
IControlEvents * events, const ControlToken& token)
|
||||
{
|
||||
PLGMPMemory mem;
|
||||
if (info.CursorShapeInfo.CursorType == IDDCX_CURSOR_SHAPE_TYPE_UNINITIALIZED)
|
||||
CSRWExclusiveLock lock(m_eventLock);
|
||||
m_events = events;
|
||||
m_token = events ? token : ControlToken {};
|
||||
}
|
||||
|
||||
PLGMPMemory CLGMPControl::FindAvailable(
|
||||
PLGMPMemory * memory, int count, int& index) const
|
||||
{
|
||||
for (int offset = 0; offset < count; ++offset)
|
||||
{
|
||||
mem = m_pointerMemory[m_pointerMemoryIndex];
|
||||
if (++m_pointerMemoryIndex == LGMP_Q_POINTER_LEN)
|
||||
m_pointerMemoryIndex = 0;
|
||||
const int candidate = (index + offset) % count;
|
||||
if (memory[candidate] &&
|
||||
!lgmpHostQueuePayloadPending(m_pointerQueue, memory[candidate]))
|
||||
{
|
||||
index = candidate;
|
||||
return memory[candidate];
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ControlResult CLGMPControl::SendCursor(
|
||||
const IDARG_OUT_QUERY_HWCURSOR& info,
|
||||
const BYTE * data, size_t size, UINT sdrWhiteLevel)
|
||||
{
|
||||
if (!m_pointerQueue)
|
||||
return ControlResult::FAILED;
|
||||
|
||||
const bool hasShape = info.CursorShapeInfo.CursorType !=
|
||||
IDDCX_CURSOR_SHAPE_TYPE_UNINITIALIZED;
|
||||
if (hasShape)
|
||||
{
|
||||
if (info.CursorShapeInfo.CursorType != IDDCX_CURSOR_SHAPE_TYPE_ALPHA &&
|
||||
info.CursorShapeInfo.CursorType !=
|
||||
IDDCX_CURSOR_SHAPE_TYPE_MASKED_COLOR)
|
||||
{
|
||||
DEBUG_ERROR("Unsupported pointer shape type: %u",
|
||||
static_cast<unsigned>(info.CursorShapeInfo.CursorType));
|
||||
return ControlResult::FAILED;
|
||||
}
|
||||
|
||||
if (info.CursorShapeInfo.Height &&
|
||||
info.CursorShapeInfo.Pitch > SIZE_MAX / info.CursorShapeInfo.Height)
|
||||
{
|
||||
DEBUG_ERROR("Pointer shape size overflow");
|
||||
return ControlResult::FAILED;
|
||||
}
|
||||
const size_t required = static_cast<size_t>(
|
||||
info.CursorShapeInfo.Height) * info.CursorShapeInfo.Pitch;
|
||||
if (required != size || (required && !data) ||
|
||||
required > MAX_POINTER_SIZE - sizeof(KVMFRCursor))
|
||||
{
|
||||
DEBUG_ERROR("Invalid pointer shape payload: %zu bytes", size);
|
||||
return ControlResult::FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
PLGMPMemory mem;
|
||||
int * index;
|
||||
int count;
|
||||
if (!hasShape)
|
||||
{
|
||||
index = &m_pointerMemoryIndex;
|
||||
count = LGMP_Q_POINTER_LEN;
|
||||
mem = FindAvailable(m_pointerMemory, count, *index);
|
||||
}
|
||||
else
|
||||
{
|
||||
mem = m_pointerShapeMemory[m_pointerShapeIndex];
|
||||
if (++m_pointerShapeIndex == POINTER_SHAPE_BUFFERS)
|
||||
m_pointerShapeIndex = 0;
|
||||
index = &m_pointerShapeIndex;
|
||||
count = POINTER_SHAPE_BUFFERS;
|
||||
mem = FindAvailable(m_pointerShapeMemory, count, *index);
|
||||
}
|
||||
if (!mem)
|
||||
return ControlResult::RETRY;
|
||||
|
||||
KVMFRCursor * cursor = (KVMFRCursor *)lgmpHostMemPtr(mem);
|
||||
cursor->sdrWhiteLevel = sdrWhiteLevel ?
|
||||
sdrWhiteLevel : KVMFR_SDR_WHITE_LEVEL_DEFAULT;
|
||||
|
||||
m_cursorVisible = info.IsCursorVisible;
|
||||
uint32_t flags = CURSOR_FLAG_VISIBLE_VALID;
|
||||
|
||||
if (info.IsCursorVisible)
|
||||
{
|
||||
m_cursorX = info.X;
|
||||
m_cursorY = info.Y;
|
||||
cursor->x = (int16_t)info.X;
|
||||
cursor->y = (int16_t)info.Y;
|
||||
flags |= CURSOR_FLAG_POSITION | CURSOR_FLAG_VISIBLE;
|
||||
}
|
||||
|
||||
if (info.CursorShapeInfo.CursorType != IDDCX_CURSOR_SHAPE_TYPE_UNINITIALIZED)
|
||||
if (hasShape)
|
||||
{
|
||||
memcpy(cursor + 1, data,
|
||||
(size_t)info.CursorShapeInfo.Height * info.CursorShapeInfo.Pitch);
|
||||
if (size)
|
||||
memcpy(cursor + 1, data, size);
|
||||
|
||||
cursor->hx = (int8_t )info.CursorShapeInfo.XHot;
|
||||
cursor->hy = (int8_t )info.CursorShapeInfo.YHot;
|
||||
@@ -185,56 +243,43 @@ void CLGMPControl::SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info,
|
||||
}
|
||||
|
||||
flags |= CURSOR_FLAG_SHAPE;
|
||||
m_pointerShape = mem;
|
||||
}
|
||||
|
||||
LGMP_STATUS status;
|
||||
while ((status = lgmpHostQueuePost(
|
||||
m_pointerQueue, flags, mem)) != LGMP_OK)
|
||||
const LGMP_STATUS status =
|
||||
lgmpHostQueuePost(m_pointerQueue, flags, mem);
|
||||
if (status == LGMP_OK)
|
||||
{
|
||||
if (status == LGMP_ERR_QUEUE_FULL)
|
||||
{
|
||||
Sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
DEBUG_ERROR("lgmpHostQueuePost Failed (Pointer): %s",
|
||||
lgmpStatusString(status));
|
||||
break;
|
||||
*index = (*index + 1) % count;
|
||||
return ControlResult::APPLIED;
|
||||
}
|
||||
if (status == LGMP_ERR_QUEUE_FULL)
|
||||
return ControlResult::RETRY;
|
||||
|
||||
DEBUG_ERROR("lgmpHostQueuePost Failed (Pointer): %s",
|
||||
lgmpStatusString(status));
|
||||
return ControlResult::FAILED;
|
||||
}
|
||||
|
||||
void CLGMPControl::SetColorTransform(
|
||||
ControlResult CLGMPControl::SetColorTransform(
|
||||
std::shared_ptr<const D12ColorTransform> transform)
|
||||
{
|
||||
{
|
||||
CSRWExclusiveLock lock(m_colorTransformLock);
|
||||
m_colorTransform = std::move(transform);
|
||||
}
|
||||
SendColorTransform();
|
||||
return SendColorTransform(transform);
|
||||
}
|
||||
|
||||
std::shared_ptr<const D12ColorTransform>
|
||||
CLGMPControl::GetColorTransform() const
|
||||
{
|
||||
CSRWSharedLock lock(m_colorTransformLock);
|
||||
std::shared_ptr<const D12ColorTransform> transform = m_colorTransform;
|
||||
return transform;
|
||||
}
|
||||
|
||||
void CLGMPControl::SendColorTransform()
|
||||
ControlResult CLGMPControl::SendColorTransform(
|
||||
const std::shared_ptr<const D12ColorTransform>& transform)
|
||||
{
|
||||
if (!m_pointerQueue || !m_pointerTransformMemory[0])
|
||||
return;
|
||||
return ControlResult::FAILED;
|
||||
|
||||
PLGMPMemory mem = m_pointerTransformMemory[m_pointerTransformIndex];
|
||||
if (++m_pointerTransformIndex == COLOR_TRANSFORM_BUFFERS)
|
||||
m_pointerTransformIndex = 0;
|
||||
PLGMPMemory mem = FindAvailable(m_pointerTransformMemory,
|
||||
COLOR_TRANSFORM_BUFFERS, m_pointerTransformIndex);
|
||||
if (!mem)
|
||||
return ControlResult::RETRY;
|
||||
|
||||
KVMFRCursor * cursor = (KVMFRCursor *)lgmpHostMemPtr(mem);
|
||||
KVMFRColorTransform * output =
|
||||
(KVMFRColorTransform *)(cursor + 1);
|
||||
const auto transform = GetColorTransform();
|
||||
|
||||
output->flags = 0;
|
||||
if (transform)
|
||||
@@ -248,54 +293,25 @@ void CLGMPControl::SendColorTransform()
|
||||
memcpy(output->lut, transform->lut, sizeof(output->lut));
|
||||
}
|
||||
|
||||
LGMP_STATUS status;
|
||||
while ((status = lgmpHostQueuePost(m_pointerQueue,
|
||||
CURSOR_FLAG_COLOR_TRANSFORM, mem)) != LGMP_OK)
|
||||
const LGMP_STATUS status = lgmpHostQueuePost(m_pointerQueue,
|
||||
CURSOR_FLAG_COLOR_TRANSFORM, mem);
|
||||
if (status == LGMP_OK)
|
||||
{
|
||||
if (status == LGMP_ERR_QUEUE_FULL)
|
||||
{
|
||||
Sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
DEBUG_ERROR("lgmpHostQueuePost Failed (Pointer Transform): %s",
|
||||
lgmpStatusString(status));
|
||||
break;
|
||||
m_pointerTransformIndex =
|
||||
(m_pointerTransformIndex + 1) % COLOR_TRANSFORM_BUFFERS;
|
||||
return ControlResult::APPLIED;
|
||||
}
|
||||
if (status == LGMP_ERR_QUEUE_FULL)
|
||||
return ControlResult::RETRY;
|
||||
|
||||
DEBUG_ERROR("lgmpHostQueuePost Failed (Pointer Transform): %s",
|
||||
lgmpStatusString(status));
|
||||
return ControlResult::FAILED;
|
||||
}
|
||||
|
||||
void CLGMPControl::ResendCursor()
|
||||
void CLGMPControl::RequestReplay()
|
||||
{
|
||||
PLGMPMemory mem = m_pointerShape;
|
||||
if (!mem)
|
||||
return;
|
||||
|
||||
KVMFRCursor* cursor = (KVMFRCursor*)lgmpHostMemPtr(mem);
|
||||
cursor->x = (int16_t)m_cursorX;
|
||||
cursor->y = (int16_t)m_cursorY;
|
||||
|
||||
const uint32_t flags =
|
||||
CURSOR_FLAG_POSITION | CURSOR_FLAG_SHAPE | CURSOR_FLAG_VISIBLE_VALID |
|
||||
(m_cursorVisible ? CURSOR_FLAG_VISIBLE : 0);
|
||||
|
||||
LGMP_STATUS status;
|
||||
while ((status = lgmpHostQueuePost(
|
||||
m_pointerQueue, flags, mem)) != LGMP_OK)
|
||||
{
|
||||
if (status == LGMP_ERR_QUEUE_FULL)
|
||||
{
|
||||
Sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
DEBUG_ERROR("lgmpHostQueuePost Failed (Pointer): %s",
|
||||
lgmpStatusString(status));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CLGMPControl::ResendState()
|
||||
{
|
||||
ResendCursor();
|
||||
SendColorTransform();
|
||||
CSRWSharedLock lock(m_eventLock);
|
||||
if (m_events)
|
||||
m_events->OnControlReplay(m_token);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "CSRWLock.h"
|
||||
|
||||
#include "transport/lgmp/CLGMPHost.h"
|
||||
#include "transport/IControlTransport.h"
|
||||
#include "transport/IControlSink.h"
|
||||
|
||||
#include "common/KVMFR.h"
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
class CLGMPTransport;
|
||||
|
||||
class CLGMPControl final : public IControlTransport
|
||||
class CLGMPControl final : public IControlSink
|
||||
{
|
||||
private:
|
||||
friend class CLGMPTransport;
|
||||
@@ -49,26 +49,25 @@ private:
|
||||
PLGMPMemory m_pointerMemory[LGMP_Q_POINTER_LEN] = {};
|
||||
PLGMPMemory m_pointerShapeMemory[POINTER_SHAPE_BUFFERS] = {};
|
||||
PLGMPMemory m_pointerTransformMemory[COLOR_TRANSFORM_BUFFERS] = {};
|
||||
PLGMPMemory m_pointerShape = nullptr;
|
||||
int m_pointerMemoryIndex = 0;
|
||||
int m_pointerShapeIndex = 0;
|
||||
int m_pointerTransformIndex = 0;
|
||||
bool m_cursorVisible = false;
|
||||
int m_cursorX = 0;
|
||||
int m_cursorY = 0;
|
||||
|
||||
mutable CSRWLock m_colorTransformLock;
|
||||
std::shared_ptr<const D12ColorTransform> m_colorTransform;
|
||||
mutable CSRWLock m_eventLock;
|
||||
IControlEvents * m_events = nullptr;
|
||||
ControlToken m_token = {};
|
||||
|
||||
void SendColorTransform();
|
||||
void ResendCursor();
|
||||
PLGMPMemory FindAvailable(
|
||||
PLGMPMemory * memory, int count, int& index) const;
|
||||
ControlResult SendColorTransform(
|
||||
const std::shared_ptr<const D12ColorTransform>& transform);
|
||||
bool Initialize();
|
||||
void DeInit();
|
||||
LGMP_STATUS ReadDataWithSource(void * data, size_t * size,
|
||||
uint32_t * sourceClientID);
|
||||
LGMP_STATUS AckData();
|
||||
bool HasNewSubscribers();
|
||||
void ResendState();
|
||||
void RequestReplay();
|
||||
|
||||
public:
|
||||
explicit CLGMPControl(CLGMPHost& host) :
|
||||
@@ -78,10 +77,10 @@ public:
|
||||
CLGMPControl(const CLGMPControl&) = delete;
|
||||
CLGMPControl& operator=(const CLGMPControl&) = delete;
|
||||
|
||||
void SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info, const BYTE * data,
|
||||
UINT sdrWhiteLevel) override;
|
||||
void SetColorTransform(
|
||||
void SetControlEvents(
|
||||
IControlEvents * events, const ControlToken& token) override;
|
||||
ControlResult SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info,
|
||||
const BYTE * data, size_t size, UINT sdrWhiteLevel) override;
|
||||
ControlResult SetColorTransform(
|
||||
std::shared_ptr<const D12ColorTransform> transform) override;
|
||||
std::shared_ptr<const D12ColorTransform>
|
||||
GetColorTransform() const override;
|
||||
};
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
#include "transport/lgmp/CLGMPInputTransport.h"
|
||||
|
||||
#include "input/IInputSink.h"
|
||||
#include "transport/lgmp/CLGMPHost.h"
|
||||
#include "CDebug.h"
|
||||
#include "CSRWLock.h"
|
||||
@@ -101,7 +100,10 @@ bool CLGMPInputTransport::Initialize()
|
||||
memset(lgmpHostMemPtr(memory), 0, sizeof(KVMFRInputStatus));
|
||||
}
|
||||
|
||||
m_statusDirty = true;
|
||||
{
|
||||
CSRWExclusiveLock lock(m_statusLock);
|
||||
m_statusDirty = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -113,26 +115,44 @@ void CLGMPInputTransport::DeInit()
|
||||
m_queue = nullptr;
|
||||
}
|
||||
|
||||
void CLGMPInputTransport::UpdateSinkState(uint64_t state)
|
||||
InputSourceId CLGMPInputTransport::Owner() const
|
||||
{
|
||||
if (state == m_sinkState)
|
||||
InputSourceId source;
|
||||
source.client = m_ownerClientID;
|
||||
source.generation = m_ownerGeneration;
|
||||
return source;
|
||||
}
|
||||
|
||||
void CLGMPInputTransport::UpdateTargetState(
|
||||
const InputTargetState& state)
|
||||
{
|
||||
CSRWExclusiveLock lock(m_statusLock);
|
||||
if (state.state == m_targetState.state &&
|
||||
state.available == m_targetState.available &&
|
||||
state.owned == m_targetState.owned &&
|
||||
state.owner.client == m_targetState.owner.client &&
|
||||
state.owner.generation == m_targetState.owner.generation)
|
||||
return;
|
||||
|
||||
m_sinkState = state;
|
||||
if (++m_endpointGeneration == 0)
|
||||
++m_endpointGeneration;
|
||||
if (state.state != m_targetState.state)
|
||||
{
|
||||
if (++m_endpointGeneration == 0)
|
||||
++m_endpointGeneration;
|
||||
}
|
||||
m_targetState = state;
|
||||
m_statusDirty = true;
|
||||
}
|
||||
|
||||
void CLGMPInputTransport::PublishStatus()
|
||||
bool CLGMPInputTransport::PublishStatus()
|
||||
{
|
||||
CSRWExclusiveLock lock(m_statusLock);
|
||||
if (!m_queue)
|
||||
return;
|
||||
return true;
|
||||
|
||||
if (lgmpHostQueueNewSubs(m_queue))
|
||||
m_statusDirty = true;
|
||||
if (!m_statusDirty || !lgmpHostQueueHasSubs(m_queue))
|
||||
return;
|
||||
return true;
|
||||
|
||||
PLGMPMemory memory = nullptr;
|
||||
for (PLGMPMemory candidate : m_statusMemory)
|
||||
@@ -142,9 +162,9 @@ void CLGMPInputTransport::PublishStatus()
|
||||
break;
|
||||
}
|
||||
if (!memory)
|
||||
return;
|
||||
return true;
|
||||
|
||||
const bool available = (m_sinkState & 1) != 0;
|
||||
const bool available = m_targetState.available;
|
||||
KVMFRInputStatus status = {};
|
||||
status.version = KVMFR_INPUT_VERSION;
|
||||
status.capabilities = available ?
|
||||
@@ -152,11 +172,11 @@ void CLGMPInputTransport::PublishStatus()
|
||||
KVMFR_INPUT_CAP_MOUSE_ABSOLUTE |
|
||||
KVMFR_INPUT_CAP_KEYBOARD : 0;
|
||||
status.flags = available ? KVMFR_INPUT_STATUS_AVAILABLE : 0;
|
||||
if (m_ownerClientID)
|
||||
if (m_targetState.owned)
|
||||
{
|
||||
status.flags |= KVMFR_INPUT_STATUS_HAS_OWNER;
|
||||
status.ownerClientID = m_ownerClientID;
|
||||
status.ownerGeneration = m_ownerGeneration;
|
||||
status.ownerClientID = m_targetState.owner.client;
|
||||
status.ownerGeneration = m_targetState.owner.generation;
|
||||
}
|
||||
status.generation = m_endpointGeneration;
|
||||
status.lease = static_cast<uint32_t>(OWNER_LEASE_MS);
|
||||
@@ -173,11 +193,26 @@ void CLGMPInputTransport::PublishStatus()
|
||||
m_statusDirty = false;
|
||||
}
|
||||
else if (result != LGMP_ERR_QUEUE_FULL)
|
||||
{
|
||||
DEBUG_WARN("lgmpHostQueuePost Failed (Input Status): %s",
|
||||
lgmpStatusString(result));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CLGMPInputTransport::Start(IInputSink& sink)
|
||||
void CLGMPInputTransport::FlushStatus()
|
||||
{
|
||||
if (m_statusFailed.load(std::memory_order_acquire) || PublishStatus())
|
||||
return;
|
||||
|
||||
m_statusFailed.store(true, std::memory_order_release);
|
||||
CSRWSharedLock lock(m_lifecycleLock);
|
||||
if (m_stopEvent)
|
||||
SetEvent(m_stopEvent);
|
||||
}
|
||||
|
||||
bool CLGMPInputTransport::Start(IInputTarget& target)
|
||||
{
|
||||
CSRWExclusiveLock lock(m_lifecycleLock);
|
||||
if (m_thread)
|
||||
@@ -198,7 +233,7 @@ bool CLGMPInputTransport::Start(IInputSink& sink)
|
||||
m_thread = nullptr;
|
||||
m_pollTimer = nullptr;
|
||||
m_stopEvent = nullptr;
|
||||
m_sink = nullptr;
|
||||
m_target = nullptr;
|
||||
}
|
||||
|
||||
if (!m_queue)
|
||||
@@ -224,19 +259,25 @@ bool CLGMPInputTransport::Start(IInputSink& sink)
|
||||
return false;
|
||||
}
|
||||
|
||||
m_sink = &sink;
|
||||
UpdateSinkState(sink.GetState());
|
||||
if (!m_endpointGeneration)
|
||||
m_target = ⌖
|
||||
{
|
||||
m_endpointGeneration = 1;
|
||||
m_statusDirty = true;
|
||||
CSRWExclusiveLock statusLock(m_statusLock);
|
||||
m_targetState = target.GetState({});
|
||||
if (++m_endpointGeneration == 0)
|
||||
++m_endpointGeneration;
|
||||
m_statusDirty = true;
|
||||
}
|
||||
m_statusFailed.store(false, std::memory_order_release);
|
||||
m_thread = CreateThread(nullptr, 0, ThreadProc, this, 0, nullptr);
|
||||
if (!m_thread)
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to create LGMP input worker");
|
||||
m_sink = nullptr;
|
||||
m_sinkState = 0;
|
||||
m_target = nullptr;
|
||||
{
|
||||
CSRWExclusiveLock statusLock(m_statusLock);
|
||||
m_targetState = {};
|
||||
m_statusDirty = true;
|
||||
}
|
||||
CloseHandle(m_pollTimer);
|
||||
CloseHandle(m_stopEvent);
|
||||
m_pollTimer = nullptr;
|
||||
@@ -271,12 +312,16 @@ void CLGMPInputTransport::Stop()
|
||||
CloseHandle(m_stopEvent);
|
||||
m_stopEvent = nullptr;
|
||||
}
|
||||
m_sink = nullptr;
|
||||
m_target = nullptr;
|
||||
m_ownerClientID = 0;
|
||||
m_ownerGeneration = 0;
|
||||
m_ownerSequence = 0;
|
||||
m_ownerDeadline = 0;
|
||||
m_sinkState = 0;
|
||||
{
|
||||
CSRWExclusiveLock statusLock(m_statusLock);
|
||||
m_targetState = {};
|
||||
m_statusDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool CLGMPInputTransport::IsOwner(
|
||||
@@ -294,16 +339,19 @@ bool CLGMPInputTransport::Claim(
|
||||
++m_statistics.sequenceErrors;
|
||||
return false;
|
||||
}
|
||||
if (!m_sink)
|
||||
if (!m_target)
|
||||
{
|
||||
++m_statistics.deliveryFailures;
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64_t sinkState = m_sink->GetState();
|
||||
if (!(sinkState & 1) || sinkState != m_sinkState ||
|
||||
!m_sink->Reset() || m_sink->GetState() != sinkState)
|
||||
InputSourceId source;
|
||||
source.client = sourceClientID;
|
||||
source.generation = message.generation;
|
||||
const InputResult result = m_target->Claim(source);
|
||||
if (result != InputResult::ACCEPTED)
|
||||
{
|
||||
UpdateTargetState(m_target->GetState(source));
|
||||
++m_statistics.deliveryFailures;
|
||||
return false;
|
||||
}
|
||||
@@ -312,7 +360,7 @@ bool CLGMPInputTransport::Claim(
|
||||
m_ownerGeneration = message.generation;
|
||||
m_ownerSequence = message.sequence;
|
||||
RenewLease();
|
||||
m_statusDirty = true;
|
||||
UpdateTargetState(m_target->GetState(source));
|
||||
++m_statistics.claims;
|
||||
DEBUG_INFO("Input owner %u generation %u acquired",
|
||||
m_ownerClientID, m_ownerGeneration);
|
||||
@@ -332,14 +380,16 @@ void CLGMPInputTransport::ReleaseOwner(
|
||||
|
||||
const uint32_t clientID = m_ownerClientID;
|
||||
const uint32_t generation = m_ownerGeneration;
|
||||
if (reset && m_sink)
|
||||
m_sink->Reset();
|
||||
if (m_target)
|
||||
{
|
||||
m_target->Release(Owner(), reset);
|
||||
UpdateTargetState(m_target->GetState({}));
|
||||
}
|
||||
|
||||
m_ownerClientID = 0;
|
||||
m_ownerGeneration = 0;
|
||||
m_ownerSequence = 0;
|
||||
m_ownerDeadline = 0;
|
||||
m_statusDirty = true;
|
||||
++m_statistics.releases;
|
||||
DEBUG_INFO("Input owner %u generation %u released (%s)",
|
||||
clientID, generation, reason);
|
||||
@@ -347,23 +397,18 @@ void CLGMPInputTransport::ReleaseOwner(
|
||||
|
||||
void CLGMPInputTransport::CheckOwner()
|
||||
{
|
||||
if (!m_sink)
|
||||
if (!m_target)
|
||||
return;
|
||||
|
||||
const uint64_t state = m_sink->GetState();
|
||||
if (state != m_sinkState)
|
||||
{
|
||||
UpdateSinkState(state);
|
||||
ReleaseOwner(true, "input endpoint changed");
|
||||
return;
|
||||
}
|
||||
const InputTargetState state = m_target->GetState(Owner());
|
||||
UpdateTargetState(state);
|
||||
|
||||
if (!m_ownerClientID)
|
||||
return;
|
||||
|
||||
if (!(state & 1))
|
||||
if (!state.available || !state.owned)
|
||||
{
|
||||
ReleaseOwner(true, "input unavailable");
|
||||
ReleaseOwner(false, "input ownership changed");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -418,17 +463,9 @@ bool CLGMPInputTransport::ProcessMessage(
|
||||
uint32_t sourceClientID, const KVMFRInputMessage& message)
|
||||
{
|
||||
const bool owner = IsOwner(sourceClientID, message.generation);
|
||||
const uint64_t sinkState = m_sink ?
|
||||
m_sink->GetState() : m_sinkState;
|
||||
if (sinkState != m_sinkState)
|
||||
{
|
||||
UpdateSinkState(sinkState);
|
||||
if (m_ownerClientID)
|
||||
{
|
||||
ReleaseOwner(true, "input endpoint changed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
InputSourceId source;
|
||||
source.client = sourceClientID;
|
||||
source.generation = message.generation;
|
||||
|
||||
if (!message.generation || !message.sequence || message.reserved ||
|
||||
!ValidatePayload(message))
|
||||
@@ -439,6 +476,10 @@ bool CLGMPInputTransport::ProcessMessage(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_target)
|
||||
UpdateTargetState(m_target->GetState(
|
||||
m_ownerClientID ? Owner() : source));
|
||||
|
||||
if (message.type == KVMFR_INPUT_MESSAGE_CLAIM)
|
||||
{
|
||||
if (m_ownerClientID)
|
||||
@@ -474,7 +515,7 @@ bool CLGMPInputTransport::ProcessMessage(
|
||||
return false;
|
||||
}
|
||||
|
||||
bool accepted = false;
|
||||
InputResult result = InputResult::STALE;
|
||||
bool inputReport = false;
|
||||
switch (message.type)
|
||||
{
|
||||
@@ -483,56 +524,52 @@ bool CLGMPInputTransport::ProcessMessage(
|
||||
return true;
|
||||
|
||||
case KVMFR_INPUT_MESSAGE_KEEPALIVE:
|
||||
accepted = true;
|
||||
result = m_target ? m_target->Touch(source) :
|
||||
InputResult::UNAVAILABLE;
|
||||
break;
|
||||
|
||||
case KVMFR_INPUT_MESSAGE_RESET:
|
||||
accepted = m_sink && m_sink->Reset();
|
||||
result = m_target ? m_target->Reset(source) :
|
||||
InputResult::UNAVAILABLE;
|
||||
break;
|
||||
|
||||
case KVMFR_INPUT_MESSAGE_MOUSE_RELATIVE:
|
||||
inputReport = true;
|
||||
accepted = m_sink && m_sink->SendMouseRelative(
|
||||
result = m_target ? m_target->SendMouseRelative(source,
|
||||
message.payload.mouseRelative.deltaX,
|
||||
message.payload.mouseRelative.deltaY,
|
||||
message.payload.mouseRelative.wheel,
|
||||
message.payload.mouseRelative.buttons);
|
||||
message.payload.mouseRelative.buttons) : InputResult::UNAVAILABLE;
|
||||
break;
|
||||
|
||||
case KVMFR_INPUT_MESSAGE_MOUSE_ABSOLUTE:
|
||||
inputReport = true;
|
||||
accepted = m_sink && m_sink->SendMouseAbsolute(
|
||||
result = m_target ? m_target->SendMouseAbsolute(source,
|
||||
message.payload.mouseAbsolute.x,
|
||||
message.payload.mouseAbsolute.y,
|
||||
message.payload.mouseAbsolute.wheel,
|
||||
message.payload.mouseAbsolute.buttons);
|
||||
message.payload.mouseAbsolute.buttons) : InputResult::UNAVAILABLE;
|
||||
break;
|
||||
|
||||
case KVMFR_INPUT_MESSAGE_KEYBOARD:
|
||||
inputReport = true;
|
||||
accepted = m_sink && m_sink->SendKeyboard(
|
||||
result = m_target ? m_target->SendKeyboard(source,
|
||||
message.payload.keyboard.modifiers,
|
||||
message.payload.keyboard.keys);
|
||||
message.payload.keyboard.keys) : InputResult::UNAVAILABLE;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!accepted)
|
||||
if (result != InputResult::ACCEPTED)
|
||||
{
|
||||
++m_statistics.deliveryFailures;
|
||||
ReleaseOwner(true, "input delivery failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint64_t deliveredState = m_sink->GetState();
|
||||
if (deliveredState != m_sinkState)
|
||||
{
|
||||
UpdateSinkState(deliveredState);
|
||||
ReleaseOwner(true, "input endpoint changed");
|
||||
return false;
|
||||
}
|
||||
UpdateTargetState(m_target->GetState(source));
|
||||
|
||||
m_ownerSequence = message.sequence;
|
||||
RenewLease();
|
||||
@@ -541,9 +578,9 @@ bool CLGMPInputTransport::ProcessMessage(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CLGMPInputTransport::DrainMessages()
|
||||
bool CLGMPInputTransport::DrainMessages(bool& received)
|
||||
{
|
||||
bool received = false;
|
||||
received = false;
|
||||
unsigned count = 0;
|
||||
for (; count < 256; ++count)
|
||||
{
|
||||
@@ -558,7 +595,7 @@ bool CLGMPInputTransport::DrainMessages()
|
||||
{
|
||||
DEBUG_ERROR("lgmpHostReadData Failed (Input): %s",
|
||||
lgmpStatusString(status));
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
|
||||
received = true;
|
||||
@@ -577,13 +614,19 @@ bool CLGMPInputTransport::DrainMessages()
|
||||
ProcessMessage(sourceClientID, message);
|
||||
}
|
||||
|
||||
lgmpHostAckData(m_queue);
|
||||
const LGMP_STATUS ackStatus = lgmpHostAckData(m_queue);
|
||||
if (ackStatus != LGMP_OK)
|
||||
{
|
||||
DEBUG_ERROR("lgmpHostAckData Failed (Input): %s",
|
||||
lgmpStatusString(ackStatus));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (count > m_statistics.maxDrain)
|
||||
m_statistics.maxDrain = count;
|
||||
if (count == 256)
|
||||
++m_statistics.drainLimit;
|
||||
return received;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CLGMPInputTransport::LogStatistics(ULONGLONG now)
|
||||
@@ -643,11 +686,22 @@ void CLGMPInputTransport::Thread()
|
||||
m_statistics = {};
|
||||
m_statistics.lastLog = GetTickCount64();
|
||||
const HANDLE waitHandles[] = { m_stopEvent, m_pollTimer };
|
||||
bool failed = false;
|
||||
for (;;)
|
||||
{
|
||||
CheckOwner();
|
||||
const bool received = DrainMessages();
|
||||
PublishStatus();
|
||||
bool received = false;
|
||||
if (!DrainMessages(received))
|
||||
{
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
if (!PublishStatus())
|
||||
{
|
||||
m_statusFailed.store(true, std::memory_order_release);
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
const ULONGLONG now = GetTickCount64();
|
||||
if (received)
|
||||
activeUntil = now + ACTIVE_POLL_MS;
|
||||
@@ -659,23 +713,30 @@ void CLGMPInputTransport::Thread()
|
||||
DEBUG_ERROR_HR(GetLastError(), "Failed to arm LGMP input timer");
|
||||
if (WaitForSingleObject(m_stopEvent, 1) != WAIT_TIMEOUT)
|
||||
break;
|
||||
continue;
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
const DWORD wait = WaitForMultipleObjects(
|
||||
_countof(waitHandles), waitHandles, FALSE, INFINITE);
|
||||
if (wait == WAIT_FIRST_OBJECT_VALUE)
|
||||
{
|
||||
failed = m_statusFailed.load(std::memory_order_acquire);
|
||||
break;
|
||||
}
|
||||
if (wait != WAIT_FIRST_OBJECT_VALUE + 1)
|
||||
{
|
||||
DEBUG_ERROR_HR(GetLastError(), "LGMP input worker wait failed");
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ReleaseOwner(true, "transport stopped");
|
||||
UpdateSinkState(0);
|
||||
UpdateTargetState({});
|
||||
PublishStatus();
|
||||
if (failed && m_target)
|
||||
m_target->Failed();
|
||||
if (avTaskHandle)
|
||||
AvRevertMmThreadCharacteristics(avTaskHandle);
|
||||
}
|
||||
|
||||
@@ -21,11 +21,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "CSRWLock.h"
|
||||
#include "transport/IInputTransport.h"
|
||||
#include "transport/IInputSource.h"
|
||||
#include "common/LGMPConfig.h"
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <stdint.h>
|
||||
|
||||
extern "C" {
|
||||
@@ -33,10 +34,10 @@ extern "C" {
|
||||
}
|
||||
|
||||
class CLGMPHost;
|
||||
class IInputSink;
|
||||
class IInputTarget;
|
||||
struct KVMFRInputMessage;
|
||||
|
||||
class CLGMPInputTransport final : public IInputTransport
|
||||
class CLGMPInputTransport final : public IInputSource
|
||||
{
|
||||
private:
|
||||
static constexpr ULONGLONG OWNER_LEASE_MS = 500;
|
||||
@@ -63,9 +64,10 @@ private:
|
||||
|
||||
PLGMPHostQueue m_queue = nullptr;
|
||||
PLGMPMemory m_statusMemory[LGMP_Q_INPUT_LEN] = {};
|
||||
IInputSink * m_sink = nullptr;
|
||||
IInputTarget * m_target = nullptr;
|
||||
|
||||
CSRWLock m_lifecycleLock;
|
||||
CSRWLock m_statusLock;
|
||||
HANDLE m_stopEvent = nullptr;
|
||||
HANDLE m_pollTimer = nullptr;
|
||||
HANDLE m_thread = nullptr;
|
||||
@@ -74,18 +76,21 @@ private:
|
||||
uint32_t m_ownerGeneration = 0;
|
||||
uint32_t m_ownerSequence = 0;
|
||||
ULONGLONG m_ownerDeadline = 0;
|
||||
uint64_t m_sinkState = 0;
|
||||
InputTargetState m_targetState;
|
||||
uint32_t m_endpointGeneration = 0;
|
||||
uint32_t m_statusSerial = 0;
|
||||
bool m_statusDirty = false;
|
||||
std::atomic<bool> m_statusFailed = false;
|
||||
Statistics m_statistics = {};
|
||||
|
||||
bool Initialize();
|
||||
void DeInit();
|
||||
void UpdateSinkState(uint64_t state);
|
||||
void PublishStatus();
|
||||
InputSourceId Owner() const;
|
||||
void UpdateTargetState(const InputTargetState& state);
|
||||
bool PublishStatus();
|
||||
void FlushStatus();
|
||||
void LogStatistics(ULONGLONG now);
|
||||
bool DrainMessages();
|
||||
bool DrainMessages(bool& received);
|
||||
bool ProcessMessage(uint32_t sourceClientID,
|
||||
const KVMFRInputMessage& message);
|
||||
bool ValidatePayload(const KVMFRInputMessage& message) const;
|
||||
@@ -109,6 +114,6 @@ public:
|
||||
CLGMPInputTransport(const CLGMPInputTransport&) = delete;
|
||||
CLGMPInputTransport& operator=(const CLGMPInputTransport&) = delete;
|
||||
|
||||
bool Start(IInputSink& sink) override;
|
||||
bool Start(IInputTarget& target) override;
|
||||
void Stop() override;
|
||||
};
|
||||
|
||||
@@ -121,6 +121,8 @@ ITransport::ProcessResult CLGMPTransport::Process(ITransportEvents& events)
|
||||
return ProcessResult::FAILURE;
|
||||
}
|
||||
|
||||
m_input.FlushStatus();
|
||||
|
||||
const uint64_t now = CFrameScheduler::Nanotime();
|
||||
|
||||
// Take the frame subscriber snapshot before processing scheduling messages,
|
||||
@@ -192,7 +194,7 @@ ITransport::ProcessResult CLGMPTransport::Process(ITransportEvents& events)
|
||||
m_frames.FinalizeSubscribers(subscribers, now);
|
||||
|
||||
if (m_control.HasNewSubscribers())
|
||||
m_control.ResendState();
|
||||
m_control.RequestReplay();
|
||||
|
||||
return ProcessResult::OK;
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
FrameMemoryLimits GetMemoryLimits() const override;
|
||||
DirectFrameBufferMemory GetDirectMemory() const override;
|
||||
|
||||
IFrameSink& FrameSink() override { return m_frames; }
|
||||
IControlTransport& Control() override { return m_control; }
|
||||
IInputTransport * Input() override { return &m_input; }
|
||||
IFrameSink * FrameSink() override { return &m_frames; }
|
||||
IControlSink * Control() override { return &m_control; }
|
||||
IInputSource * Input() override { return &m_input; }
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user