mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 07:01:30 +00:00
726 lines
19 KiB
C++
726 lines
19 KiB
C++
/**
|
|
* Looking Glass
|
|
* Copyright © 2017-2026 The Looking Glass Authors
|
|
* https://looking-glass.io
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
* Software Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "transport/CInputHub.h"
|
|
|
|
#include "input/IInputSink.h"
|
|
#include "Seq.h"
|
|
|
|
static bool SameClient(const SourceKey& left, const SourceKey& right)
|
|
{
|
|
return
|
|
left.backend == right.backend &&
|
|
left.epoch == right.epoch &&
|
|
left.client == right.client;
|
|
}
|
|
|
|
CInputHub::CInputHub()
|
|
{
|
|
for (Source& source : m_sources)
|
|
source.owner = this;
|
|
}
|
|
|
|
CInputHub::~CInputHub()
|
|
{
|
|
Stop();
|
|
for (Source& source : m_sources)
|
|
{
|
|
BackendId backend;
|
|
uint32_t epoch;
|
|
{
|
|
CSRWSharedLock lock(m_lock);
|
|
backend = source.backend;
|
|
epoch = source.epoch;
|
|
}
|
|
if (backend && epoch)
|
|
Unbind(backend, epoch);
|
|
}
|
|
}
|
|
|
|
SourceKey CInputHub::Source::Key(const InputSourceId& sourceId) const
|
|
{
|
|
SourceKey source;
|
|
source.backend = backend;
|
|
source.epoch = epoch;
|
|
source.client = sourceId.client;
|
|
source.generation = sourceId.generation;
|
|
return source;
|
|
}
|
|
|
|
InputTargetState CInputHub::Source::GetState(
|
|
const InputSourceId& source)
|
|
{
|
|
return owner->GetState(Key(source));
|
|
}
|
|
|
|
void CInputHub::Source::Failed()
|
|
{
|
|
owner->Failed(*this);
|
|
}
|
|
|
|
InputResult CInputHub::Source::Claim(const InputSourceId& source)
|
|
{
|
|
return owner->Claim(Key(source));
|
|
}
|
|
|
|
InputResult CInputHub::Source::Touch(const InputSourceId& source)
|
|
{
|
|
return owner->Touch(Key(source));
|
|
}
|
|
|
|
InputResult CInputHub::Source::Release(
|
|
const InputSourceId& source, bool reset)
|
|
{
|
|
return owner->Release(Key(source), reset);
|
|
}
|
|
|
|
InputResult CInputHub::Source::SendMouseRelative(
|
|
const InputSourceId& source,
|
|
int32_t deltaX, int32_t deltaY, int32_t wheel, uint32_t buttons)
|
|
{
|
|
return owner->SendMouseRelative(
|
|
Key(source), deltaX, deltaY, wheel, buttons);
|
|
}
|
|
|
|
InputResult CInputHub::Source::SendMouseAbsolute(
|
|
const InputSourceId& source,
|
|
uint16_t x, uint16_t y, int32_t wheel, uint32_t buttons)
|
|
{
|
|
return owner->SendMouseAbsolute(Key(source), x, y, wheel, buttons);
|
|
}
|
|
|
|
InputResult CInputHub::Source::SendKeyboard(const InputSourceId& source,
|
|
uint8_t modifiers, const uint8_t * keys)
|
|
{
|
|
return owner->SendKeyboard(Key(source), modifiers, keys);
|
|
}
|
|
|
|
InputResult CInputHub::Source::Reset(const InputSourceId& source)
|
|
{
|
|
return owner->Reset(Key(source));
|
|
}
|
|
|
|
bool CInputHub::Bind(
|
|
BackendId backend, uint32_t epoch, IInputSource& input)
|
|
{
|
|
CSRWExclusiveLock lifecycleLock(m_lifecycleLock);
|
|
if (!backend || !epoch)
|
|
return false;
|
|
|
|
Source * selected = nullptr;
|
|
bool start = false;
|
|
{
|
|
CSRWExclusiveLock lock(m_lock);
|
|
for (Source& source : m_sources)
|
|
if ((source.active || source.failed || source.reserved) &&
|
|
source.backend == backend && source.epoch == epoch)
|
|
return false;
|
|
for (Source& source : m_sources)
|
|
if (!source.active && !source.failed && !source.reserved)
|
|
{
|
|
source.backend = backend;
|
|
source.epoch = epoch;
|
|
source.endpoint = &input;
|
|
source.active = true;
|
|
source.reserved = true;
|
|
selected = &source;
|
|
start = m_started;
|
|
break;
|
|
}
|
|
}
|
|
if (!selected)
|
|
return false;
|
|
|
|
if (start && !input.Start(*selected))
|
|
{
|
|
input.Stop();
|
|
CSRWExclusiveLock lock(m_lock);
|
|
if (selected->reserved && selected->endpoint == &input &&
|
|
selected->backend == backend && selected->epoch == epoch)
|
|
{
|
|
selected->backend = 0;
|
|
selected->epoch = 0;
|
|
selected->endpoint = nullptr;
|
|
selected->running = false;
|
|
selected->active = false;
|
|
selected->failed = false;
|
|
selected->failurePending = false;
|
|
selected->reserved = false;
|
|
}
|
|
return false;
|
|
}
|
|
{
|
|
CSRWExclusiveLock lock(m_lock);
|
|
if (!selected->reserved || selected->endpoint != &input ||
|
|
selected->backend != backend || selected->epoch != epoch ||
|
|
selected->failed)
|
|
{
|
|
lock.Unlock();
|
|
if (start)
|
|
input.Stop();
|
|
{
|
|
CSRWExclusiveLock clearLock(m_lock);
|
|
if (selected->endpoint == &input &&
|
|
selected->backend == backend && selected->epoch == epoch)
|
|
{
|
|
selected->backend = 0;
|
|
selected->epoch = 0;
|
|
selected->endpoint = nullptr;
|
|
selected->running = false;
|
|
selected->active = false;
|
|
selected->failed = false;
|
|
selected->failurePending = false;
|
|
selected->reserved = false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
selected->running = start;
|
|
selected->reserved = false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CInputHub::Unbind(BackendId backend, uint32_t epoch)
|
|
{
|
|
CSRWExclusiveLock lifecycleLock(m_lifecycleLock);
|
|
Source * selected = nullptr;
|
|
IInputSource * input = nullptr;
|
|
bool stop = false;
|
|
{
|
|
CSRWExclusiveLock lock(m_lock);
|
|
for (Source& source : m_sources)
|
|
if ((source.active || source.failed || source.reserved) &&
|
|
source.backend == backend && source.epoch == epoch)
|
|
{
|
|
source.active = false;
|
|
source.failed = false;
|
|
source.failurePending = false;
|
|
source.reserved = true;
|
|
input = source.endpoint;
|
|
stop = source.running;
|
|
source.running = false;
|
|
selected = &source;
|
|
if (m_owner.backend == backend && m_owner.epoch == epoch)
|
|
{
|
|
if (m_sink)
|
|
m_sink->Reset();
|
|
m_owner = {};
|
|
m_ownerDeadline = 0;
|
|
}
|
|
if (m_interactionOwner.backend == backend &&
|
|
m_interactionOwner.epoch == epoch)
|
|
{
|
|
ClearInteraction();
|
|
}
|
|
AdvanceInteractionSerial();
|
|
break;
|
|
}
|
|
}
|
|
if (!selected)
|
|
return;
|
|
|
|
if (input && stop)
|
|
input->Stop();
|
|
|
|
CSRWExclusiveLock lock(m_lock);
|
|
if (selected->reserved && selected->backend == backend &&
|
|
selected->epoch == epoch)
|
|
{
|
|
selected->backend = 0;
|
|
selected->epoch = 0;
|
|
selected->endpoint = nullptr;
|
|
selected->running = false;
|
|
selected->reserved = false;
|
|
selected->failed = false;
|
|
selected->failurePending = false;
|
|
}
|
|
}
|
|
|
|
InteractionResult CInputHub::CheckInteraction(
|
|
SourceKey& source, InteractionPermit& permit)
|
|
{
|
|
permit = {};
|
|
if (!source.backend || !source.epoch || !source.client)
|
|
return InteractionResult::STALE;
|
|
|
|
CSRWExclusiveLock lock(m_lock);
|
|
if (m_started && m_sink)
|
|
CheckState();
|
|
|
|
if (m_owner.backend)
|
|
{
|
|
if (!SameClient(m_owner, source))
|
|
return InteractionResult::BUSY;
|
|
if (source.generation && source.generation != m_owner.generation)
|
|
return InteractionResult::BUSY;
|
|
source.generation = m_owner.generation;
|
|
permit.serial = m_interactionSerial;
|
|
permit.source = source;
|
|
return InteractionResult::ACCEPTED;
|
|
}
|
|
|
|
const uint64_t now = GetTickCount64();
|
|
if (m_interactionOwner.backend && now >= m_interactionDeadline)
|
|
InvalidateInteraction();
|
|
|
|
if (!m_interactionOwner.backend)
|
|
{
|
|
permit.serial = m_interactionSerial;
|
|
permit.source = source;
|
|
return InteractionResult::ACCEPTED;
|
|
}
|
|
|
|
if (!SameClient(m_interactionOwner, source) ||
|
|
(source.generation && m_interactionOwner.generation &&
|
|
source.generation != m_interactionOwner.generation))
|
|
return InteractionResult::BUSY;
|
|
|
|
if (!source.generation)
|
|
source.generation = m_interactionOwner.generation;
|
|
permit.serial = m_interactionSerial;
|
|
permit.source = source;
|
|
return InteractionResult::ACCEPTED;
|
|
}
|
|
|
|
void CInputHub::CommitInteraction(
|
|
const SourceKey& source, const InteractionPermit& permit)
|
|
{
|
|
if (!source.backend || !source.epoch || !source.client || !permit.serial ||
|
|
source != permit.source)
|
|
return;
|
|
|
|
CSRWExclusiveLock lock(m_lock);
|
|
if (m_started && m_sink)
|
|
CheckState();
|
|
if (permit.serial != m_interactionSerial)
|
|
return;
|
|
if (m_owner.backend)
|
|
return;
|
|
|
|
const uint64_t now = GetTickCount64();
|
|
if (m_interactionOwner.backend &&
|
|
(!SameClient(m_interactionOwner, source) ||
|
|
(source.generation && m_interactionOwner.generation &&
|
|
source.generation != m_interactionOwner.generation)))
|
|
return;
|
|
|
|
if (!m_interactionOwner.backend)
|
|
m_interactionOwner = source;
|
|
else if (!m_interactionOwner.generation)
|
|
m_interactionOwner.generation = source.generation;
|
|
m_interactionDeadline = now + INTERACTION_LEASE_MS;
|
|
AdvanceInteractionSerial();
|
|
}
|
|
|
|
void CInputHub::RevokeInteraction(BackendId backend, uint32_t epoch)
|
|
{
|
|
if (!backend || !epoch)
|
|
return;
|
|
|
|
CSRWExclusiveLock lock(m_lock);
|
|
if (m_interactionOwner.backend == backend &&
|
|
m_interactionOwner.epoch == epoch)
|
|
ClearInteraction();
|
|
AdvanceInteractionSerial();
|
|
}
|
|
|
|
bool CInputHub::TakeFailure(SourceKey& source)
|
|
{
|
|
CSRWSharedLock lifecycleLock(m_lifecycleLock);
|
|
CSRWExclusiveLock lock(m_lock);
|
|
for (Source& slot : m_sources)
|
|
{
|
|
if (!slot.failurePending)
|
|
continue;
|
|
source.backend = slot.backend;
|
|
source.epoch = slot.epoch;
|
|
slot.failurePending = false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CInputHub::Start(IInputSink& sink)
|
|
{
|
|
CSRWExclusiveLock lifecycleLock(m_lifecycleLock);
|
|
Source * sources[MAX_SOURCES] = {};
|
|
unsigned count = 0;
|
|
{
|
|
CSRWExclusiveLock lock(m_lock);
|
|
if (m_started)
|
|
return m_sink == &sink;
|
|
m_sink = &sink;
|
|
m_sinkState = sink.GetState();
|
|
m_started = true;
|
|
InvalidateInteraction();
|
|
for (Source& source : m_sources)
|
|
if (source.active)
|
|
{
|
|
source.reserved = true;
|
|
sources[count++] = &source;
|
|
}
|
|
}
|
|
|
|
for (unsigned i = 0; i < count; ++i)
|
|
{
|
|
Source& source = *sources[i];
|
|
const bool started = source.endpoint->Start(source);
|
|
bool stop = !started;
|
|
{
|
|
CSRWExclusiveLock lock(m_lock);
|
|
if (!started || source.failed || !source.active || !source.reserved)
|
|
{
|
|
if (m_owner.backend == source.backend &&
|
|
m_owner.epoch == source.epoch)
|
|
{
|
|
if (m_sink)
|
|
m_sink->Reset();
|
|
m_owner = {};
|
|
m_ownerDeadline = 0;
|
|
}
|
|
if (m_interactionOwner.backend == source.backend &&
|
|
m_interactionOwner.epoch == source.epoch)
|
|
{
|
|
ClearInteraction();
|
|
}
|
|
AdvanceInteractionSerial();
|
|
source.active = false;
|
|
source.failed = true;
|
|
source.failurePending = true;
|
|
stop = true;
|
|
}
|
|
else
|
|
source.reserved = false;
|
|
source.running = started && !stop;
|
|
}
|
|
|
|
if (!stop)
|
|
continue;
|
|
source.endpoint->Stop();
|
|
{
|
|
CSRWExclusiveLock lock(m_lock);
|
|
source.running = false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CInputHub::Stop()
|
|
{
|
|
CSRWExclusiveLock lifecycleLock(m_lifecycleLock);
|
|
Source * sources[MAX_SOURCES] = {};
|
|
unsigned count = 0;
|
|
IInputSink * sink = nullptr;
|
|
bool reset = false;
|
|
{
|
|
CSRWExclusiveLock lock(m_lock);
|
|
if (!m_started)
|
|
{
|
|
InvalidateInteraction();
|
|
return;
|
|
}
|
|
m_started = false;
|
|
for (Source& source : m_sources)
|
|
if (source.endpoint && source.running)
|
|
{
|
|
source.running = false;
|
|
sources[count++] = &source;
|
|
}
|
|
sink = m_sink;
|
|
reset = m_owner.backend != 0;
|
|
m_owner = {};
|
|
m_ownerDeadline = 0;
|
|
InvalidateInteraction();
|
|
}
|
|
|
|
for (unsigned i = count; i > 0; --i)
|
|
sources[i - 1]->endpoint->Stop();
|
|
if (reset && sink)
|
|
sink->Reset();
|
|
|
|
CSRWExclusiveLock lock(m_lock);
|
|
m_sink = nullptr;
|
|
m_sinkState = 0;
|
|
}
|
|
|
|
bool CInputHub::SourceValid(const SourceKey& source) const
|
|
{
|
|
if (!source.client || !source.generation)
|
|
return false;
|
|
return BindingValid(source);
|
|
}
|
|
|
|
bool CInputHub::BindingValid(const SourceKey& source) const
|
|
{
|
|
if (!BindingPresent(source))
|
|
return false;
|
|
for (const Source& slot : m_sources)
|
|
if (slot.active && !slot.reserved && slot.endpoint &&
|
|
slot.backend == source.backend &&
|
|
slot.epoch == source.epoch)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool CInputHub::BindingPresent(const SourceKey& source) const
|
|
{
|
|
if (!source.backend || !source.epoch)
|
|
return false;
|
|
for (const Source& slot : m_sources)
|
|
if (slot.active && slot.endpoint &&
|
|
slot.backend == source.backend &&
|
|
slot.epoch == source.epoch)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool CInputHub::OwnerValid(const SourceKey& source) const
|
|
{
|
|
return SourceValid(source) && m_owner == source;
|
|
}
|
|
|
|
void CInputHub::ClearInteraction()
|
|
{
|
|
m_interactionOwner = {};
|
|
m_interactionDeadline = 0;
|
|
}
|
|
|
|
void CInputHub::InvalidateInteraction()
|
|
{
|
|
ClearInteraction();
|
|
AdvanceInteractionSerial();
|
|
}
|
|
|
|
void CInputHub::AdvanceInteractionSerial()
|
|
{
|
|
Seq::Inc(m_interactionSerial);
|
|
}
|
|
|
|
bool CInputHub::CheckState()
|
|
{
|
|
if (!m_started || !m_sink)
|
|
return false;
|
|
|
|
const uint64_t state = m_sink->GetState();
|
|
if (state != m_sinkState)
|
|
{
|
|
if (m_owner.backend)
|
|
m_sink->Reset();
|
|
m_owner = {};
|
|
m_ownerDeadline = 0;
|
|
InvalidateInteraction();
|
|
m_sinkState = state;
|
|
}
|
|
else if (m_owner.backend && GetTickCount64() >= m_ownerDeadline)
|
|
{
|
|
m_sink->Reset();
|
|
m_owner = {};
|
|
m_ownerDeadline = 0;
|
|
InvalidateInteraction();
|
|
}
|
|
return (state & 1) != 0;
|
|
}
|
|
|
|
InputTargetState CInputHub::GetState(const SourceKey& source)
|
|
{
|
|
CSRWExclusiveLock lock(m_lock);
|
|
InputTargetState result;
|
|
if (!BindingPresent(source))
|
|
return result;
|
|
const bool available = CheckState();
|
|
result.state = m_sinkState;
|
|
if (!available)
|
|
return result;
|
|
if (!BindingValid(source))
|
|
return result;
|
|
if (!m_owner.backend)
|
|
result.available = true;
|
|
else if (source.client && source.generation && m_owner == source)
|
|
{
|
|
result.available = true;
|
|
result.owned = true;
|
|
result.owner.client = m_owner.client;
|
|
result.owner.generation = m_owner.generation;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void CInputHub::Failed(Source& source)
|
|
{
|
|
CSRWExclusiveLock lock(m_lock);
|
|
if (!source.active)
|
|
return;
|
|
if (m_owner.backend == source.backend && m_owner.epoch == source.epoch)
|
|
{
|
|
if (m_sink)
|
|
m_sink->Reset();
|
|
m_owner = {};
|
|
m_ownerDeadline = 0;
|
|
}
|
|
if (m_interactionOwner.backend == source.backend &&
|
|
m_interactionOwner.epoch == source.epoch)
|
|
{
|
|
ClearInteraction();
|
|
}
|
|
AdvanceInteractionSerial();
|
|
source.active = false;
|
|
source.failed = true;
|
|
source.failurePending = true;
|
|
}
|
|
|
|
InputResult CInputHub::Claim(const SourceKey& source)
|
|
{
|
|
CSRWExclusiveLock lock(m_lock);
|
|
if (!SourceValid(source))
|
|
return InputResult::STALE;
|
|
if (!CheckState())
|
|
return InputResult::UNAVAILABLE;
|
|
if (m_owner.backend)
|
|
return m_owner == source ?
|
|
InputResult::ACCEPTED : InputResult::BUSY;
|
|
if (!m_sink->Reset() || m_sink->GetState() != m_sinkState)
|
|
{
|
|
const uint64_t state = m_sink->GetState();
|
|
m_sinkState = state;
|
|
InvalidateInteraction();
|
|
return InputResult::UNAVAILABLE;
|
|
}
|
|
m_owner = source;
|
|
m_ownerDeadline = GetTickCount64() + OWNER_LEASE_MS;
|
|
InvalidateInteraction();
|
|
return InputResult::ACCEPTED;
|
|
}
|
|
|
|
InputResult CInputHub::Touch(const SourceKey& source)
|
|
{
|
|
CSRWExclusiveLock lock(m_lock);
|
|
if (!SourceValid(source))
|
|
return InputResult::STALE;
|
|
if (!CheckState())
|
|
return InputResult::UNAVAILABLE;
|
|
if (!OwnerValid(source))
|
|
return m_owner.backend ? InputResult::BUSY : InputResult::STALE;
|
|
m_ownerDeadline = GetTickCount64() + OWNER_LEASE_MS;
|
|
return InputResult::ACCEPTED;
|
|
}
|
|
|
|
InputResult CInputHub::Release(const SourceKey& source, bool reset)
|
|
{
|
|
CSRWExclusiveLock lock(m_lock);
|
|
if (!SourceValid(source))
|
|
return InputResult::STALE;
|
|
if (!OwnerValid(source))
|
|
return m_owner.backend ? InputResult::BUSY : InputResult::STALE;
|
|
bool accepted = true;
|
|
if (reset && m_sink)
|
|
accepted = m_sink->Reset();
|
|
m_owner = {};
|
|
m_ownerDeadline = 0;
|
|
InvalidateInteraction();
|
|
return accepted ? InputResult::ACCEPTED : InputResult::UNAVAILABLE;
|
|
}
|
|
|
|
InputResult CInputHub::SendMouseRelative(const SourceKey& source,
|
|
int32_t deltaX, int32_t deltaY, int32_t wheel, uint32_t buttons)
|
|
{
|
|
CSRWExclusiveLock lock(m_lock);
|
|
if (!SourceValid(source))
|
|
return InputResult::STALE;
|
|
if (!CheckState())
|
|
return InputResult::UNAVAILABLE;
|
|
if (!OwnerValid(source))
|
|
return m_owner.backend ? InputResult::BUSY : InputResult::STALE;
|
|
if (!m_sink->SendMouseRelative(deltaX, deltaY, wheel, buttons))
|
|
{
|
|
m_sink->Reset();
|
|
m_owner = {};
|
|
m_ownerDeadline = 0;
|
|
InvalidateInteraction();
|
|
return InputResult::UNAVAILABLE;
|
|
}
|
|
m_ownerDeadline = GetTickCount64() + OWNER_LEASE_MS;
|
|
return InputResult::ACCEPTED;
|
|
}
|
|
|
|
InputResult CInputHub::SendMouseAbsolute(const SourceKey& source,
|
|
uint16_t x, uint16_t y, int32_t wheel, uint32_t buttons)
|
|
{
|
|
CSRWExclusiveLock lock(m_lock);
|
|
if (!SourceValid(source))
|
|
return InputResult::STALE;
|
|
if (!CheckState())
|
|
return InputResult::UNAVAILABLE;
|
|
if (!OwnerValid(source))
|
|
return m_owner.backend ? InputResult::BUSY : InputResult::STALE;
|
|
if (!m_sink->SendMouseAbsolute(x, y, wheel, buttons))
|
|
{
|
|
m_sink->Reset();
|
|
m_owner = {};
|
|
m_ownerDeadline = 0;
|
|
InvalidateInteraction();
|
|
return InputResult::UNAVAILABLE;
|
|
}
|
|
m_ownerDeadline = GetTickCount64() + OWNER_LEASE_MS;
|
|
return InputResult::ACCEPTED;
|
|
}
|
|
|
|
InputResult CInputHub::SendKeyboard(const SourceKey& source,
|
|
uint8_t modifiers, const uint8_t * keys)
|
|
{
|
|
CSRWExclusiveLock lock(m_lock);
|
|
if (!SourceValid(source))
|
|
return InputResult::STALE;
|
|
if (!CheckState())
|
|
return InputResult::UNAVAILABLE;
|
|
if (!OwnerValid(source))
|
|
return m_owner.backend ? InputResult::BUSY : InputResult::STALE;
|
|
if (!m_sink->SendKeyboard(modifiers, keys))
|
|
{
|
|
m_sink->Reset();
|
|
m_owner = {};
|
|
m_ownerDeadline = 0;
|
|
InvalidateInteraction();
|
|
return InputResult::UNAVAILABLE;
|
|
}
|
|
m_ownerDeadline = GetTickCount64() + OWNER_LEASE_MS;
|
|
return InputResult::ACCEPTED;
|
|
}
|
|
|
|
InputResult CInputHub::Reset(const SourceKey& source)
|
|
{
|
|
CSRWExclusiveLock lock(m_lock);
|
|
if (!SourceValid(source))
|
|
return InputResult::STALE;
|
|
if (!CheckState())
|
|
return InputResult::UNAVAILABLE;
|
|
if (!OwnerValid(source))
|
|
return m_owner.backend ? InputResult::BUSY : InputResult::STALE;
|
|
if (!m_sink->Reset())
|
|
{
|
|
m_owner = {};
|
|
m_ownerDeadline = 0;
|
|
InvalidateInteraction();
|
|
return InputResult::UNAVAILABLE;
|
|
}
|
|
m_ownerDeadline = GetTickCount64() + OWNER_LEASE_MS;
|
|
return InputResult::ACCEPTED;
|
|
}
|