mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 15:11:31 +00:00
[idd] lgmp: route clipboard file operations
This commit is contained in:
@@ -78,6 +78,7 @@
|
|||||||
<ClCompile Include="transport\lgmp\CIVSHMEM.cpp" />
|
<ClCompile Include="transport\lgmp\CIVSHMEM.cpp" />
|
||||||
<ClCompile Include="transport\lgmp\CRecovery.cpp" />
|
<ClCompile Include="transport\lgmp\CRecovery.cpp" />
|
||||||
<ClCompile Include="transport\lgmp\CLGMPClipboardTransport.cpp" />
|
<ClCompile Include="transport\lgmp\CLGMPClipboardTransport.cpp" />
|
||||||
|
<ClCompile Include="transport\lgmp\CLGMPClipboardFiles.cpp" />
|
||||||
<ClCompile Include="transport\lgmp\CLGMPControl.cpp" />
|
<ClCompile Include="transport\lgmp\CLGMPControl.cpp" />
|
||||||
<ClCompile Include="transport\lgmp\CLGMPFrameCaps.cpp" />
|
<ClCompile Include="transport\lgmp\CLGMPFrameCaps.cpp" />
|
||||||
<ClCompile Include="transport\lgmp\CLGMPFrameTransport.cpp" />
|
<ClCompile Include="transport\lgmp\CLGMPFrameTransport.cpp" />
|
||||||
@@ -154,6 +155,7 @@
|
|||||||
<ClInclude Include="transport\lgmp\CIVSHMEM.h" />
|
<ClInclude Include="transport\lgmp\CIVSHMEM.h" />
|
||||||
<ClInclude Include="transport\lgmp\CRecovery.h" />
|
<ClInclude Include="transport\lgmp\CRecovery.h" />
|
||||||
<ClInclude Include="transport\lgmp\CLGMPClipboardTransport.h" />
|
<ClInclude Include="transport\lgmp\CLGMPClipboardTransport.h" />
|
||||||
|
<ClInclude Include="transport\lgmp\CLGMPClipboardFiles.h" />
|
||||||
<ClInclude Include="transport\lgmp\CLGMPControl.h" />
|
<ClInclude Include="transport\lgmp\CLGMPControl.h" />
|
||||||
<ClInclude Include="transport\lgmp\CLGMPFrameCaps.h" />
|
<ClInclude Include="transport\lgmp\CLGMPFrameCaps.h" />
|
||||||
<ClInclude Include="transport\lgmp\CLGMPFrameTransport.h" />
|
<ClInclude Include="transport\lgmp\CLGMPFrameTransport.h" />
|
||||||
|
|||||||
@@ -250,6 +250,9 @@
|
|||||||
<ClInclude Include="transport\lgmp\CLGMPClipboardTransport.h">
|
<ClInclude Include="transport\lgmp\CLGMPClipboardTransport.h">
|
||||||
<Filter>Transport\LGMP</Filter>
|
<Filter>Transport\LGMP</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="transport\lgmp\CLGMPClipboardFiles.h">
|
||||||
|
<Filter>Transport\LGMP</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="transport\lgmp\CLGMPControl.h">
|
<ClInclude Include="transport\lgmp\CLGMPControl.h">
|
||||||
<Filter>Transport\LGMP</Filter>
|
<Filter>Transport\LGMP</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -414,6 +417,9 @@
|
|||||||
<ClCompile Include="transport\lgmp\CLGMPClipboardTransport.cpp">
|
<ClCompile Include="transport\lgmp\CLGMPClipboardTransport.cpp">
|
||||||
<Filter>Transport\LGMP</Filter>
|
<Filter>Transport\LGMP</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="transport\lgmp\CLGMPClipboardFiles.cpp">
|
||||||
|
<Filter>Transport\LGMP</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="transport\lgmp\CLGMPControl.cpp">
|
<ClCompile Include="transport\lgmp\CLGMPControl.cpp">
|
||||||
<Filter>Transport\LGMP</Filter>
|
<Filter>Transport\LGMP</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|||||||
431
idd/LGIdd/transport/lgmp/CLGMPClipboardFiles.cpp
Normal file
431
idd/LGIdd/transport/lgmp/CLGMPClipboardFiles.cpp
Normal file
@@ -0,0 +1,431 @@
|
|||||||
|
/**
|
||||||
|
* 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/lgmp/CLGMPClipboardFiles.h"
|
||||||
|
|
||||||
|
bool CLGMPClipboardFiles::FromHelper(Direction direction)
|
||||||
|
{
|
||||||
|
return direction == Direction::HELPER_TO_CLIENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLGMPClipboardFiles::RequesterHelper(uint64_t transfer)
|
||||||
|
{
|
||||||
|
return kvmfrClipboardTransferFromHelper(transfer) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLGMPClipboardFiles::SameRequester(
|
||||||
|
Direction direction, uint64_t transfer)
|
||||||
|
{
|
||||||
|
return FromHelper(direction) == RequesterHelper(transfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
CLGMPClipboardFiles::Acquisition *
|
||||||
|
CLGMPClipboardFiles::FindAcquisition(uint64_t transfer)
|
||||||
|
{
|
||||||
|
for (Acquisition& acquisition : m_acquisitions)
|
||||||
|
if (acquisition.transfer == transfer)
|
||||||
|
return &acquisition;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CLGMPClipboardFiles::Acquisition *
|
||||||
|
CLGMPClipboardFiles::FindAcquisition(uint64_t transfer) const
|
||||||
|
{
|
||||||
|
for (const Acquisition& acquisition : m_acquisitions)
|
||||||
|
if (acquisition.transfer == transfer)
|
||||||
|
return &acquisition;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CLGMPClipboardFiles::Acquisition *
|
||||||
|
CLGMPClipboardFiles::FindAcquired(
|
||||||
|
uint64_t dataset, bool requesterHelper) const
|
||||||
|
{
|
||||||
|
for (const Acquisition& acquisition : m_acquisitions)
|
||||||
|
if (acquisition.dataset == dataset && acquisition.requesterHelper ==
|
||||||
|
requesterHelper && acquisition.acquired)
|
||||||
|
return &acquisition;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLGMPClipboardFiles::HasAcquisition(
|
||||||
|
uint64_t dataset, bool requesterHelper) const
|
||||||
|
{
|
||||||
|
for (const Acquisition& acquisition : m_acquisitions)
|
||||||
|
if (acquisition.dataset == dataset &&
|
||||||
|
acquisition.requesterHelper == requesterHelper)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CLGMPClipboardFiles::Acquisition * CLGMPClipboardFiles::FreeAcquisition()
|
||||||
|
{
|
||||||
|
for (Acquisition& acquisition : m_acquisitions)
|
||||||
|
if (!acquisition.Active())
|
||||||
|
return &acquisition;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLGMPClipboardFiles::HasFreeAcquisition() const
|
||||||
|
{
|
||||||
|
for (const Acquisition& acquisition : m_acquisitions)
|
||||||
|
if (!acquisition.Active())
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CLGMPClipboardFiles::Request * CLGMPClipboardFiles::FindRequest(
|
||||||
|
uint64_t transfer)
|
||||||
|
{
|
||||||
|
for (Request& request : m_requests)
|
||||||
|
if (request.transfer == transfer)
|
||||||
|
return &request;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CLGMPClipboardFiles::Request * CLGMPClipboardFiles::FindRequest(
|
||||||
|
uint64_t transfer) const
|
||||||
|
{
|
||||||
|
for (const Request& request : m_requests)
|
||||||
|
if (request.transfer == transfer)
|
||||||
|
return &request;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
CLGMPClipboardFiles::Request * CLGMPClipboardFiles::FreeRequest()
|
||||||
|
{
|
||||||
|
for (Request& request : m_requests)
|
||||||
|
if (!request.Active())
|
||||||
|
return &request;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLGMPClipboardFiles::HasFreeRequest() const
|
||||||
|
{
|
||||||
|
for (const Request& request : m_requests)
|
||||||
|
if (!request.Active())
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLGMPClipboardFiles::TransferInUse(uint64_t transfer) const
|
||||||
|
{
|
||||||
|
return FindAcquisition(transfer) || FindRequest(transfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLGMPClipboardFiles::ValidateData(const Request& request,
|
||||||
|
const KVMFRClipboardMessage& message) const
|
||||||
|
{
|
||||||
|
if (message.clipboardGeneration != request.dataset ||
|
||||||
|
message.token != request.operation ||
|
||||||
|
message.offset != request.expectedOffset ||
|
||||||
|
message.sequence != request.expectedSequence)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const uint64_t end = message.offset + message.length;
|
||||||
|
if (!request.began)
|
||||||
|
{
|
||||||
|
if (message.offset || message.sequence ||
|
||||||
|
!(message.flags & KVMFR_CLIPBOARD_FLAG_BEGIN))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (message.flags & KVMFR_CLIPBOARD_FLAG_BEGIN)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const uint64_t hint = request.began ? request.sizeHint : message.size;
|
||||||
|
if (hint != KVMFR_CLIPBOARD_SIZE_UNKNOWN && end > hint)
|
||||||
|
return false;
|
||||||
|
if (request.operation == KVMFR_CLIPBOARD_FILE_OP_READ &&
|
||||||
|
end > request.requestedBytes)
|
||||||
|
return false;
|
||||||
|
if (message.flags & KVMFR_CLIPBOARD_FLAG_END)
|
||||||
|
return message.size == end &&
|
||||||
|
(hint == KVMFR_CLIPBOARD_SIZE_UNKNOWN || hint == end);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLGMPClipboardFiles::RemoveAcquisition(Acquisition& acquisition)
|
||||||
|
{
|
||||||
|
const uint64_t dataset = acquisition.dataset;
|
||||||
|
const bool requesterHelper = acquisition.requesterHelper;
|
||||||
|
acquisition.Clear();
|
||||||
|
for (Request& request : m_requests)
|
||||||
|
if (request.dataset == dataset &&
|
||||||
|
request.requesterHelper == requesterHelper)
|
||||||
|
request.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLGMPClipboardFiles::IsRecord(const KVMFRClipboardMessage& message)
|
||||||
|
{
|
||||||
|
return kvmfrClipboardFileMessageType(message.type) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLGMPClipboardFiles::IsData(const KVMFRClipboardMessage& message)
|
||||||
|
{
|
||||||
|
return message.type == KVMFR_CLIPBOARD_MESSAGE_FILE_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLGMPClipboardFiles::DirectionValid(
|
||||||
|
const KVMFRClipboardMessage& message, Direction direction)
|
||||||
|
{
|
||||||
|
if (!kvmfrClipboardFileMessageValid(&message))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (message.type)
|
||||||
|
{
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_ACQUIRE:
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_RELEASE:
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_REQUEST:
|
||||||
|
return SameRequester(direction, message.transfer);
|
||||||
|
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_ACQUIRED:
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_DATA:
|
||||||
|
return !SameRequester(direction, message.transfer);
|
||||||
|
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_CANCEL:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLGMPClipboardFiles::TransferActive(uint64_t transfer) const
|
||||||
|
{
|
||||||
|
return TransferInUse(transfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLGMPClipboardFiles::IsStaleTerminal(
|
||||||
|
const KVMFRClipboardMessage& message, Direction direction) const
|
||||||
|
{
|
||||||
|
if (!DirectionValid(message, direction) ||
|
||||||
|
TransferInUse(message.transfer))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (message.type)
|
||||||
|
{
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_ACQUIRED:
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_DATA:
|
||||||
|
return !SameRequester(direction, message.transfer);
|
||||||
|
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_RELEASE:
|
||||||
|
return SameRequester(direction, message.transfer);
|
||||||
|
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_CANCEL:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLGMPClipboardFiles::Validate(const KVMFRClipboardMessage& message,
|
||||||
|
Direction direction, uint64_t offeredDataset, bool filesOffered) const
|
||||||
|
{
|
||||||
|
if (!DirectionValid(message, direction))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (message.type)
|
||||||
|
{
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_ACQUIRE:
|
||||||
|
return SameRequester(direction, message.transfer) && filesOffered &&
|
||||||
|
message.clipboardGeneration == offeredDataset &&
|
||||||
|
!TransferInUse(message.transfer) &&
|
||||||
|
!HasAcquisition(message.clipboardGeneration,
|
||||||
|
FromHelper(direction)) && HasFreeAcquisition();
|
||||||
|
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_ACQUIRED:
|
||||||
|
{
|
||||||
|
const Acquisition * acquisition =
|
||||||
|
FindAcquisition(message.transfer);
|
||||||
|
return acquisition && !acquisition->acquired &&
|
||||||
|
acquisition->dataset == message.clipboardGeneration &&
|
||||||
|
FromHelper(direction) != acquisition->requesterHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_RELEASE:
|
||||||
|
{
|
||||||
|
const Acquisition * acquisition =
|
||||||
|
FindAcquisition(message.transfer);
|
||||||
|
return acquisition && acquisition->acquired &&
|
||||||
|
acquisition->dataset == message.clipboardGeneration &&
|
||||||
|
FromHelper(direction) == acquisition->requesterHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_REQUEST:
|
||||||
|
return SameRequester(direction, message.transfer) &&
|
||||||
|
!TransferInUse(message.transfer) && HasFreeRequest() &&
|
||||||
|
FindAcquired(message.clipboardGeneration,
|
||||||
|
FromHelper(direction));
|
||||||
|
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_DATA:
|
||||||
|
{
|
||||||
|
const Request * request = FindRequest(message.transfer);
|
||||||
|
return request &&
|
||||||
|
FromHelper(direction) != request->requesterHelper &&
|
||||||
|
ValidateData(*request, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_CANCEL:
|
||||||
|
{
|
||||||
|
const Acquisition * acquisition =
|
||||||
|
FindAcquisition(message.transfer);
|
||||||
|
if (acquisition)
|
||||||
|
return acquisition->dataset == message.clipboardGeneration;
|
||||||
|
const Request * request = FindRequest(message.transfer);
|
||||||
|
return request && request->dataset == message.clipboardGeneration;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLGMPClipboardFiles::Apply(
|
||||||
|
const KVMFRClipboardMessage& message, Direction direction)
|
||||||
|
{
|
||||||
|
switch (message.type)
|
||||||
|
{
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_ACQUIRE:
|
||||||
|
{
|
||||||
|
Acquisition * acquisition = FreeAcquisition();
|
||||||
|
if (!acquisition)
|
||||||
|
return;
|
||||||
|
acquisition->dataset = message.clipboardGeneration;
|
||||||
|
acquisition->transfer = message.transfer;
|
||||||
|
acquisition->requesterHelper = FromHelper(direction);
|
||||||
|
acquisition->acquired = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_ACQUIRED:
|
||||||
|
{
|
||||||
|
Acquisition * acquisition = FindAcquisition(message.transfer);
|
||||||
|
if (!acquisition)
|
||||||
|
return;
|
||||||
|
if (message.token == KVMFR_CLIPBOARD_FILE_ERROR_NONE)
|
||||||
|
acquisition->acquired = true;
|
||||||
|
else
|
||||||
|
RemoveAcquisition(*acquisition);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_RELEASE:
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_CANCEL:
|
||||||
|
{
|
||||||
|
Acquisition * acquisition = FindAcquisition(message.transfer);
|
||||||
|
if (acquisition)
|
||||||
|
{
|
||||||
|
RemoveAcquisition(*acquisition);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Request * request = FindRequest(message.transfer);
|
||||||
|
if (request)
|
||||||
|
request->Clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_REQUEST:
|
||||||
|
{
|
||||||
|
Request * request = FreeRequest();
|
||||||
|
if (!request)
|
||||||
|
return;
|
||||||
|
request->dataset = message.clipboardGeneration;
|
||||||
|
request->transfer = message.transfer;
|
||||||
|
request->operation = message.token;
|
||||||
|
request->requestedBytes = message.token ==
|
||||||
|
KVMFR_CLIPBOARD_FILE_OP_READ ? message.flags : 0;
|
||||||
|
request->requesterHelper = FromHelper(direction);
|
||||||
|
request->sizeHint = KVMFR_CLIPBOARD_SIZE_UNKNOWN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_DATA:
|
||||||
|
{
|
||||||
|
Request * request = FindRequest(message.transfer);
|
||||||
|
if (!request)
|
||||||
|
return;
|
||||||
|
if (!request->began)
|
||||||
|
{
|
||||||
|
request->began = true;
|
||||||
|
request->sizeHint = message.size;
|
||||||
|
}
|
||||||
|
request->expectedOffset += message.length;
|
||||||
|
++request->expectedSequence;
|
||||||
|
if (message.flags & KVMFR_CLIPBOARD_FLAG_END)
|
||||||
|
request->Clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLGMPClipboardFiles::Reset()
|
||||||
|
{
|
||||||
|
for (Acquisition& acquisition : m_acquisitions)
|
||||||
|
acquisition.Clear();
|
||||||
|
for (Request& request : m_requests)
|
||||||
|
request.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned CLGMPClipboardFiles::Disconnect(KVMFRClipboardMessage * records,
|
||||||
|
unsigned capacity, uint32_t endpointGeneration)
|
||||||
|
{
|
||||||
|
unsigned count = 0;
|
||||||
|
for (const Request& request : m_requests)
|
||||||
|
{
|
||||||
|
if (!request.Active() || count == capacity)
|
||||||
|
continue;
|
||||||
|
KVMFRClipboardMessage& record = records[count++];
|
||||||
|
record = {};
|
||||||
|
record.version = KVMFR_CLIPBOARD_VERSION;
|
||||||
|
record.type = KVMFR_CLIPBOARD_MESSAGE_FILE_CANCEL;
|
||||||
|
record.generation = endpointGeneration;
|
||||||
|
record.clipboardGeneration = request.dataset;
|
||||||
|
record.transfer = request.transfer;
|
||||||
|
record.format = KVMFR_CLIPBOARD_FORMAT_FILES;
|
||||||
|
record.token = request.requesterHelper ?
|
||||||
|
KVMFR_CLIPBOARD_FILE_ERROR_DISCONNECTED :
|
||||||
|
KVMFR_CLIPBOARD_FILE_ERROR_CANCELLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const Acquisition& acquisition : m_acquisitions)
|
||||||
|
{
|
||||||
|
if (!acquisition.Active() || count == capacity)
|
||||||
|
continue;
|
||||||
|
KVMFRClipboardMessage& record = records[count++];
|
||||||
|
record = {};
|
||||||
|
record.version = KVMFR_CLIPBOARD_VERSION;
|
||||||
|
record.type = acquisition.requesterHelper ?
|
||||||
|
(acquisition.acquired ? KVMFR_CLIPBOARD_MESSAGE_FILE_CANCEL :
|
||||||
|
KVMFR_CLIPBOARD_MESSAGE_FILE_ACQUIRED) :
|
||||||
|
(acquisition.acquired ? KVMFR_CLIPBOARD_MESSAGE_FILE_RELEASE :
|
||||||
|
KVMFR_CLIPBOARD_MESSAGE_FILE_CANCEL);
|
||||||
|
record.generation = endpointGeneration;
|
||||||
|
record.clipboardGeneration = acquisition.dataset;
|
||||||
|
record.transfer = acquisition.transfer;
|
||||||
|
record.format = KVMFR_CLIPBOARD_FORMAT_FILES;
|
||||||
|
if (acquisition.requesterHelper)
|
||||||
|
record.token = KVMFR_CLIPBOARD_FILE_ERROR_DISCONNECTED;
|
||||||
|
else if (!acquisition.acquired)
|
||||||
|
record.token = KVMFR_CLIPBOARD_FILE_ERROR_CANCELLED;
|
||||||
|
}
|
||||||
|
Reset();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
114
idd/LGIdd/transport/lgmp/CLGMPClipboardFiles.h
Normal file
114
idd/LGIdd/transport/lgmp/CLGMPClipboardFiles.h
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/KVMFRClipboard.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
class CLGMPClipboardFiles
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class Direction
|
||||||
|
{
|
||||||
|
CLIENT_TO_HELPER,
|
||||||
|
HELPER_TO_CLIENT,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr unsigned MAX_ACQUISITIONS =
|
||||||
|
KVMFR_CLIPBOARD_FILE_MAX_ACQUISITIONS;
|
||||||
|
static constexpr unsigned MAX_REQUESTS =
|
||||||
|
KVMFR_CLIPBOARD_FILE_MAX_REQUESTS;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Acquisition
|
||||||
|
{
|
||||||
|
uint64_t dataset = 0;
|
||||||
|
uint64_t transfer = 0;
|
||||||
|
bool requesterHelper = false;
|
||||||
|
bool acquired = false;
|
||||||
|
|
||||||
|
bool Active() const { return transfer != 0; }
|
||||||
|
void Clear() { *this = {}; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Request
|
||||||
|
{
|
||||||
|
uint64_t dataset = 0;
|
||||||
|
uint64_t transfer = 0;
|
||||||
|
uint64_t expectedOffset = 0;
|
||||||
|
uint64_t sizeHint = KVMFR_CLIPBOARD_SIZE_UNKNOWN;
|
||||||
|
uint32_t expectedSequence = 0;
|
||||||
|
uint32_t requestedBytes = 0;
|
||||||
|
KVMFRClipboardFileOperation operation = 0;
|
||||||
|
bool requesterHelper = false;
|
||||||
|
bool began = false;
|
||||||
|
|
||||||
|
bool Active() const { return transfer != 0; }
|
||||||
|
void Clear() { *this = {}; }
|
||||||
|
};
|
||||||
|
|
||||||
|
Acquisition m_acquisitions[MAX_ACQUISITIONS] = {};
|
||||||
|
Request m_requests[MAX_REQUESTS] = {};
|
||||||
|
|
||||||
|
static bool FromHelper(Direction direction);
|
||||||
|
static bool RequesterHelper(uint64_t transfer);
|
||||||
|
static bool SameRequester(Direction direction, uint64_t transfer);
|
||||||
|
|
||||||
|
Acquisition * FindAcquisition(uint64_t transfer);
|
||||||
|
const Acquisition * FindAcquisition(uint64_t transfer) const;
|
||||||
|
const Acquisition * FindAcquired(
|
||||||
|
uint64_t dataset, bool requesterHelper) const;
|
||||||
|
bool HasAcquisition(uint64_t dataset, bool requesterHelper) const;
|
||||||
|
Acquisition * FreeAcquisition();
|
||||||
|
bool HasFreeAcquisition() const;
|
||||||
|
|
||||||
|
Request * FindRequest(uint64_t transfer);
|
||||||
|
const Request * FindRequest(uint64_t transfer) const;
|
||||||
|
Request * FreeRequest();
|
||||||
|
bool HasFreeRequest() const;
|
||||||
|
|
||||||
|
bool TransferInUse(uint64_t transfer) const;
|
||||||
|
bool ValidateData(const Request& request,
|
||||||
|
const KVMFRClipboardMessage& message) const;
|
||||||
|
void RemoveAcquisition(Acquisition& acquisition);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static bool IsRecord(const KVMFRClipboardMessage& message);
|
||||||
|
static bool IsData(const KVMFRClipboardMessage& message);
|
||||||
|
static bool DirectionValid(const KVMFRClipboardMessage& message,
|
||||||
|
Direction direction);
|
||||||
|
bool TransferActive(uint64_t transfer) const;
|
||||||
|
bool IsStaleTerminal(const KVMFRClipboardMessage& message,
|
||||||
|
Direction direction) const;
|
||||||
|
|
||||||
|
bool Validate(const KVMFRClipboardMessage& message,
|
||||||
|
Direction direction, uint64_t offeredDataset,
|
||||||
|
bool filesOffered) const;
|
||||||
|
void Apply(const KVMFRClipboardMessage& message, Direction direction);
|
||||||
|
void Reset();
|
||||||
|
|
||||||
|
// Builds the records which release Helper-owned resources or fail Helper
|
||||||
|
// requests when an LGMP clipboard owner disappears. The state is reset even
|
||||||
|
// when capacity is smaller than the number of generated records.
|
||||||
|
unsigned Disconnect(KVMFRClipboardMessage * records, unsigned capacity,
|
||||||
|
uint32_t endpointGeneration);
|
||||||
|
};
|
||||||
@@ -75,7 +75,7 @@ namespace
|
|||||||
kvmfrClipboardTransferFromHelper(message.transfer) :
|
kvmfrClipboardTransferFromHelper(message.transfer) :
|
||||||
kvmfrClipboardTransferFromClient(message.transfer);
|
kvmfrClipboardTransferFromClient(message.transfer);
|
||||||
return message.clipboardGeneration && validTransfer &&
|
return message.clipboardGeneration && validTransfer &&
|
||||||
kvmfrClipboardFormatValid(message.format) &&
|
kvmfrClipboardRepresentationFormatValid(message.format) &&
|
||||||
!message.offset && !message.size && !message.flags &&
|
!message.offset && !message.size && !message.flags &&
|
||||||
!message.token && !message.length && !message.sequence;
|
!message.token && !message.length && !message.sequence;
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,8 @@ namespace
|
|||||||
bool ValidCancel(const KVMFRClipboardMessage& message)
|
bool ValidCancel(const KVMFRClipboardMessage& message)
|
||||||
{
|
{
|
||||||
return message.transfer && !message.offset && !message.size &&
|
return message.transfer && !message.offset && !message.size &&
|
||||||
(!message.format || kvmfrClipboardFormatValid(message.format)) &&
|
(!message.format ||
|
||||||
|
kvmfrClipboardRepresentationFormatValid(message.format)) &&
|
||||||
!message.flags && !message.length && !message.sequence;
|
!message.flags && !message.length && !message.sequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +92,8 @@ namespace
|
|||||||
{
|
{
|
||||||
return message.type == KVMFR_CLIPBOARD_MESSAGE_REQUEST ||
|
return message.type == KVMFR_CLIPBOARD_MESSAGE_REQUEST ||
|
||||||
message.type == KVMFR_CLIPBOARD_MESSAGE_DATA ||
|
message.type == KVMFR_CLIPBOARD_MESSAGE_DATA ||
|
||||||
message.type == KVMFR_CLIPBOARD_MESSAGE_CANCEL;
|
message.type == KVMFR_CLIPBOARD_MESSAGE_CANCEL ||
|
||||||
|
CLGMPClipboardFiles::IsRecord(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,6 +369,39 @@ void CLGMPClipboardTransport::QueueStaleRequestCancel(
|
|||||||
QueueInternalTarget(cancel);
|
QueueInternalTarget(cancel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CLGMPClipboardTransport::QueueStaleFileRecord(
|
||||||
|
const KVMFRClipboardMessage& record)
|
||||||
|
{
|
||||||
|
if (!m_target || !m_available || !m_endpointGeneration)
|
||||||
|
return;
|
||||||
|
|
||||||
|
KVMFRClipboardMessage response = {};
|
||||||
|
response.version = KVMFR_CLIPBOARD_VERSION;
|
||||||
|
response.generation = m_endpointGeneration;
|
||||||
|
response.clipboardGeneration = record.clipboardGeneration;
|
||||||
|
response.transfer = record.transfer;
|
||||||
|
response.format = KVMFR_CLIPBOARD_FORMAT_FILES;
|
||||||
|
response.token = KVMFR_CLIPBOARD_FILE_ERROR_DISCONNECTED;
|
||||||
|
if (record.type == KVMFR_CLIPBOARD_MESSAGE_FILE_ACQUIRE)
|
||||||
|
response.type = KVMFR_CLIPBOARD_MESSAGE_FILE_ACQUIRED;
|
||||||
|
else if (record.type == KVMFR_CLIPBOARD_MESSAGE_FILE_REQUEST)
|
||||||
|
response.type = KVMFR_CLIPBOARD_MESSAGE_FILE_CANCEL;
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
QueueInternalTarget(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLGMPClipboardTransport::QueueFileDisconnect()
|
||||||
|
{
|
||||||
|
KVMFRClipboardMessage records[
|
||||||
|
CLGMPClipboardFiles::MAX_ACQUISITIONS +
|
||||||
|
CLGMPClipboardFiles::MAX_REQUESTS] = {};
|
||||||
|
const unsigned count = m_files.Disconnect(records, _countof(records),
|
||||||
|
m_endpointGeneration);
|
||||||
|
for (unsigned i = 0; i < count; ++i)
|
||||||
|
QueueInternalTarget(records[i]);
|
||||||
|
}
|
||||||
|
|
||||||
bool CLGMPClipboardTransport::QueueInternalTarget(
|
bool CLGMPClipboardTransport::QueueInternalTarget(
|
||||||
const KVMFRClipboardMessage& record)
|
const KVMFRClipboardMessage& record)
|
||||||
{
|
{
|
||||||
@@ -423,12 +458,14 @@ void CLGMPClipboardTransport::ReleaseOwner(
|
|||||||
clientID, generation, reason);
|
clientID, generation, reason);
|
||||||
if (clearHelper)
|
if (clearHelper)
|
||||||
{
|
{
|
||||||
|
QueueFileDisconnect();
|
||||||
QueueTransferCancel(clientToHelper);
|
QueueTransferCancel(clientToHelper);
|
||||||
QueueTransferCancel(helperToClient);
|
QueueTransferCancel(helperToClient);
|
||||||
QueueHelperClear();
|
QueueHelperClear();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
m_files.Reset();
|
||||||
m_clientClipboardGeneration = 0;
|
m_clientClipboardGeneration = 0;
|
||||||
m_clientFormats = 0;
|
m_clientFormats = 0;
|
||||||
}
|
}
|
||||||
@@ -441,6 +478,7 @@ void CLGMPClipboardTransport::ResetProtocol(bool keepClipboard)
|
|||||||
ReleaseOwner("endpoint reset", false);
|
ReleaseOwner("endpoint reset", false);
|
||||||
m_clientToHelper.Clear();
|
m_clientToHelper.Clear();
|
||||||
m_helperToClient.Clear();
|
m_helperToClient.Clear();
|
||||||
|
m_files.Reset();
|
||||||
m_clientClipboardGeneration = 0;
|
m_clientClipboardGeneration = 0;
|
||||||
m_clientFormats = 0;
|
m_clientFormats = 0;
|
||||||
m_discardHelperToClient = 0;
|
m_discardHelperToClient = 0;
|
||||||
@@ -661,10 +699,18 @@ bool CLGMPClipboardTransport::ValidateOwnedControl(
|
|||||||
case KVMFR_CLIPBOARD_MESSAGE_CANCEL:
|
case KVMFR_CLIPBOARD_MESSAGE_CANCEL:
|
||||||
return ValidateInboundRecord(message);
|
return ValidateInboundRecord(message);
|
||||||
|
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_ACQUIRE:
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_ACQUIRED:
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_RELEASE:
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_REQUEST:
|
||||||
|
case KVMFR_CLIPBOARD_MESSAGE_FILE_CANCEL:
|
||||||
|
return ValidateInboundRecord(message);
|
||||||
|
|
||||||
case KVMFR_CLIPBOARD_MESSAGE_COMMIT:
|
case KVMFR_CLIPBOARD_MESSAGE_COMMIT:
|
||||||
return message.token >= 1 && message.token <= MEMORY_COUNT &&
|
return message.token >= 1 && message.token <= MEMORY_COUNT &&
|
||||||
message.clipboardGeneration && message.transfer &&
|
message.clipboardGeneration && message.transfer &&
|
||||||
kvmfrClipboardFormatValid(message.format) &&
|
(kvmfrClipboardRepresentationFormatValid(message.format) ||
|
||||||
|
message.format == KVMFR_CLIPBOARD_FORMAT_FILES) &&
|
||||||
message.length <= KVMFR_CLIPBOARD_DATA_BYTES &&
|
message.length <= KVMFR_CLIPBOARD_DATA_BYTES &&
|
||||||
!(message.flags & ~(KVMFR_CLIPBOARD_FLAG_BEGIN |
|
!(message.flags & ~(KVMFR_CLIPBOARD_FLAG_BEGIN |
|
||||||
KVMFR_CLIPBOARD_FLAG_END)) &&
|
KVMFR_CLIPBOARD_FLAG_END)) &&
|
||||||
@@ -678,6 +724,21 @@ bool CLGMPClipboardTransport::ValidateOwnedControl(
|
|||||||
bool CLGMPClipboardTransport::ValidateInboundRecord(
|
bool CLGMPClipboardTransport::ValidateInboundRecord(
|
||||||
const KVMFRClipboardMessage& message) const
|
const KVMFRClipboardMessage& message) const
|
||||||
{
|
{
|
||||||
|
if (CLGMPClipboardFiles::IsRecord(message))
|
||||||
|
{
|
||||||
|
if (m_files.IsStaleTerminal(message,
|
||||||
|
CLGMPClipboardFiles::Direction::CLIENT_TO_HELPER))
|
||||||
|
return true;
|
||||||
|
const bool filesOffered = m_cachedValid &&
|
||||||
|
m_cachedClipboard.type == KVMFR_CLIPBOARD_MESSAGE_OFFER &&
|
||||||
|
(m_helperFormats & KVMFR_CLIPBOARD_FORMAT_MASK_FILES);
|
||||||
|
const uint64_t offeredDataset = filesOffered ?
|
||||||
|
m_cachedClipboard.clipboardGeneration : 0;
|
||||||
|
return m_files.Validate(message,
|
||||||
|
CLGMPClipboardFiles::Direction::CLIENT_TO_HELPER,
|
||||||
|
offeredDataset, filesOffered);
|
||||||
|
}
|
||||||
|
|
||||||
switch (message.type)
|
switch (message.type)
|
||||||
{
|
{
|
||||||
case KVMFR_CLIPBOARD_MESSAGE_OFFER:
|
case KVMFR_CLIPBOARD_MESSAGE_OFFER:
|
||||||
@@ -704,6 +765,15 @@ bool CLGMPClipboardTransport::ValidateOutboundRecord(
|
|||||||
message.generation != m_endpointGeneration)
|
message.generation != m_endpointGeneration)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (CLGMPClipboardFiles::IsRecord(message))
|
||||||
|
{
|
||||||
|
const bool filesOffered = m_clientClipboardGeneration &&
|
||||||
|
(m_clientFormats & KVMFR_CLIPBOARD_FORMAT_MASK_FILES);
|
||||||
|
return m_files.Validate(message,
|
||||||
|
CLGMPClipboardFiles::Direction::HELPER_TO_CLIENT,
|
||||||
|
m_clientClipboardGeneration, filesOffered);
|
||||||
|
}
|
||||||
|
|
||||||
switch (message.type)
|
switch (message.type)
|
||||||
{
|
{
|
||||||
case KVMFR_CLIPBOARD_MESSAGE_OFFER:
|
case KVMFR_CLIPBOARD_MESSAGE_OFFER:
|
||||||
@@ -718,7 +788,7 @@ bool CLGMPClipboardTransport::ValidateOutboundRecord(
|
|||||||
case KVMFR_CLIPBOARD_MESSAGE_DATA:
|
case KVMFR_CLIPBOARD_MESSAGE_DATA:
|
||||||
return message.clipboardGeneration &&
|
return message.clipboardGeneration &&
|
||||||
kvmfrClipboardTransferFromClient(message.transfer) &&
|
kvmfrClipboardTransferFromClient(message.transfer) &&
|
||||||
kvmfrClipboardFormatValid(message.format) &&
|
kvmfrClipboardRepresentationFormatValid(message.format) &&
|
||||||
message.length <= KVMFR_CLIPBOARD_DATA_BYTES &&
|
message.length <= KVMFR_CLIPBOARD_DATA_BYTES &&
|
||||||
!(message.flags & ~(KVMFR_CLIPBOARD_FLAG_BEGIN |
|
!(message.flags & ~(KVMFR_CLIPBOARD_FLAG_BEGIN |
|
||||||
KVMFR_CLIPBOARD_FLAG_END)) && !message.token &&
|
KVMFR_CLIPBOARD_FLAG_END)) && !message.token &&
|
||||||
@@ -786,6 +856,13 @@ void CLGMPClipboardTransport::AdvanceChunk(
|
|||||||
void CLGMPClipboardTransport::ApplyInbound(
|
void CLGMPClipboardTransport::ApplyInbound(
|
||||||
const KVMFRClipboardMessage& message)
|
const KVMFRClipboardMessage& message)
|
||||||
{
|
{
|
||||||
|
if (CLGMPClipboardFiles::IsRecord(message))
|
||||||
|
{
|
||||||
|
m_files.Apply(message,
|
||||||
|
CLGMPClipboardFiles::Direction::CLIENT_TO_HELPER);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (message.type)
|
switch (message.type)
|
||||||
{
|
{
|
||||||
case KVMFR_CLIPBOARD_MESSAGE_OFFER:
|
case KVMFR_CLIPBOARD_MESSAGE_OFFER:
|
||||||
@@ -824,6 +901,13 @@ void CLGMPClipboardTransport::ApplyInbound(
|
|||||||
void CLGMPClipboardTransport::ApplyOutbound(
|
void CLGMPClipboardTransport::ApplyOutbound(
|
||||||
const KVMFRClipboardMessage& message)
|
const KVMFRClipboardMessage& message)
|
||||||
{
|
{
|
||||||
|
if (CLGMPClipboardFiles::IsRecord(message))
|
||||||
|
{
|
||||||
|
m_files.Apply(message,
|
||||||
|
CLGMPClipboardFiles::Direction::HELPER_TO_CLIENT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (message.type)
|
switch (message.type)
|
||||||
{
|
{
|
||||||
case KVMFR_CLIPBOARD_MESSAGE_OFFER:
|
case KVMFR_CLIPBOARD_MESSAGE_OFFER:
|
||||||
@@ -977,6 +1061,22 @@ bool CLGMPClipboardTransport::ProcessMessage(
|
|||||||
|
|
||||||
if (!IsOwner(clientID, message.generation))
|
if (!IsOwner(clientID, message.generation))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (CLGMPClipboardFiles::IsRecord(message) &&
|
||||||
|
CLGMPClipboardFiles::DirectionValid(message,
|
||||||
|
CLGMPClipboardFiles::Direction::CLIENT_TO_HELPER) &&
|
||||||
|
!m_files.TransferActive(message.transfer) &&
|
||||||
|
(message.type == KVMFR_CLIPBOARD_MESSAGE_FILE_ACQUIRE ||
|
||||||
|
message.type == KVMFR_CLIPBOARD_MESSAGE_FILE_REQUEST) &&
|
||||||
|
!ValidateInboundRecord(message))
|
||||||
|
{
|
||||||
|
// A cancellation or replacement can cross a request which was generated
|
||||||
|
// from the preceding file offer. The already-posted lifecycle record will
|
||||||
|
// cancel the client-side work; consuming this stale request avoids
|
||||||
|
// treating normal cross-queue ordering as a protocol failure.
|
||||||
|
RenewLease();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (!ValidateOwnedControl(message))
|
if (!ValidateOwnedControl(message))
|
||||||
{
|
{
|
||||||
ReleaseOwner("invalid clipboard message", true);
|
ReleaseOwner("invalid clipboard message", true);
|
||||||
@@ -1009,9 +1109,12 @@ bool CLGMPClipboardTransport::ProcessMessage(
|
|||||||
lgmpHostMemPtr(m_grantMemory[grantIndex]));
|
lgmpHostMemPtr(m_grantMemory[grantIndex]));
|
||||||
KVMFRClipboardMessage dataMessage = {};
|
KVMFRClipboardMessage dataMessage = {};
|
||||||
memcpy(&dataMessage, slot, sizeof(dataMessage));
|
memcpy(&dataMessage, slot, sizeof(dataMessage));
|
||||||
|
const bool fileData = dataMessage.type ==
|
||||||
|
KVMFR_CLIPBOARD_MESSAGE_FILE_DATA;
|
||||||
const bool matchesCommit =
|
const bool matchesCommit =
|
||||||
dataMessage.version == KVMFR_CLIPBOARD_VERSION &&
|
dataMessage.version == KVMFR_CLIPBOARD_VERSION &&
|
||||||
dataMessage.type == KVMFR_CLIPBOARD_MESSAGE_DATA &&
|
(dataMessage.type == KVMFR_CLIPBOARD_MESSAGE_DATA ||
|
||||||
|
fileData) &&
|
||||||
dataMessage.generation == m_ownerGeneration &&
|
dataMessage.generation == m_ownerGeneration &&
|
||||||
dataMessage.clipboardGeneration == message.clipboardGeneration &&
|
dataMessage.clipboardGeneration == message.clipboardGeneration &&
|
||||||
dataMessage.transfer == message.transfer &&
|
dataMessage.transfer == message.transfer &&
|
||||||
@@ -1020,16 +1123,28 @@ bool CLGMPClipboardTransport::ProcessMessage(
|
|||||||
dataMessage.format == message.format &&
|
dataMessage.format == message.format &&
|
||||||
dataMessage.flags == message.flags &&
|
dataMessage.flags == message.flags &&
|
||||||
dataMessage.length == message.length &&
|
dataMessage.length == message.length &&
|
||||||
dataMessage.sequence == message.sequence &&
|
dataMessage.sequence == message.sequence;
|
||||||
!dataMessage.token;
|
const bool staleFileData = fileData && m_files.IsStaleTerminal(
|
||||||
if (!matchesCommit ||
|
dataMessage, CLGMPClipboardFiles::Direction::CLIENT_TO_HELPER);
|
||||||
!kvmfrClipboardTransferFromHelper(dataMessage.transfer) ||
|
const bool validData = fileData ?
|
||||||
!ValidateChunk(m_clientToHelper, dataMessage))
|
(staleFileData || m_files.Validate(dataMessage,
|
||||||
|
CLGMPClipboardFiles::Direction::CLIENT_TO_HELPER, 0, false)) :
|
||||||
|
(!dataMessage.token &&
|
||||||
|
kvmfrClipboardTransferFromHelper(dataMessage.transfer) &&
|
||||||
|
ValidateChunk(m_clientToHelper, dataMessage));
|
||||||
|
if (!matchesCommit || !validData)
|
||||||
{
|
{
|
||||||
ReleaseOwner("invalid clipboard commit", true);
|
ReleaseOwner("invalid clipboard commit", true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (staleFileData)
|
||||||
|
{
|
||||||
|
grant = {};
|
||||||
|
RenewLease();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
grant.committed = true;
|
grant.committed = true;
|
||||||
dataMessage.generation = m_endpointGeneration;
|
dataMessage.generation = m_endpointGeneration;
|
||||||
RenewLease();
|
RenewLease();
|
||||||
@@ -1039,6 +1154,13 @@ bool CLGMPClipboardTransport::ProcessMessage(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_files.IsStaleTerminal(message,
|
||||||
|
CLGMPClipboardFiles::Direction::CLIENT_TO_HELPER))
|
||||||
|
{
|
||||||
|
RenewLease();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (message.type == KVMFR_CLIPBOARD_MESSAGE_REQUEST &&
|
if (message.type == KVMFR_CLIPBOARD_MESSAGE_REQUEST &&
|
||||||
m_helperToClient.Active())
|
m_helperToClient.Active())
|
||||||
{
|
{
|
||||||
@@ -1144,7 +1266,11 @@ ClipboardChannelResult CLGMPClipboardTransport::SendControl(
|
|||||||
ClipboardChannelResult CLGMPClipboardTransport::SendData(
|
ClipboardChannelResult CLGMPClipboardTransport::SendData(
|
||||||
const KVMFRClipboardMessage& record, const uint8_t * data)
|
const KVMFRClipboardMessage& record, const uint8_t * data)
|
||||||
{
|
{
|
||||||
if (!ValidateChunk(m_helperToClient, record))
|
const bool valid = CLGMPClipboardFiles::IsData(record) ?
|
||||||
|
m_files.Validate(record,
|
||||||
|
CLGMPClipboardFiles::Direction::HELPER_TO_CLIENT, 0, false) :
|
||||||
|
ValidateChunk(m_helperToClient, record);
|
||||||
|
if (!valid)
|
||||||
return ClipboardChannelResult::FAILED;
|
return ClipboardChannelResult::FAILED;
|
||||||
|
|
||||||
PLGMPMemory memory = FindAvailable(m_dataMemory);
|
PLGMPMemory memory = FindAvailable(m_dataMemory);
|
||||||
@@ -1192,6 +1318,7 @@ ClipboardChannelResult CLGMPClipboardTransport::SendClipboard(
|
|||||||
if (BlockedOwnerLost(record))
|
if (BlockedOwnerLost(record))
|
||||||
{
|
{
|
||||||
QueueStaleRequestCancel(record);
|
QueueStaleRequestCancel(record);
|
||||||
|
QueueStaleFileRecord(record);
|
||||||
ClearOutboundBlock();
|
ClearOutboundBlock();
|
||||||
Wake();
|
Wake();
|
||||||
return ClipboardChannelResult::ACCEPTED;
|
return ClipboardChannelResult::ACCEPTED;
|
||||||
@@ -1201,8 +1328,31 @@ ClipboardChannelResult CLGMPClipboardTransport::SendClipboard(
|
|||||||
record.generation != m_endpointGeneration)
|
record.generation != m_endpointGeneration)
|
||||||
return ClipboardChannelResult::FAILED;
|
return ClipboardChannelResult::FAILED;
|
||||||
|
|
||||||
if (!ValidateOutboundRecord(record) ||
|
if (m_files.IsStaleTerminal(record,
|
||||||
(record.length && !data))
|
CLGMPClipboardFiles::Direction::HELPER_TO_CLIENT))
|
||||||
|
{
|
||||||
|
ClearOutboundBlock();
|
||||||
|
return ClipboardChannelResult::ACCEPTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool validOutbound = ValidateOutboundRecord(record);
|
||||||
|
if (!validOutbound && CLGMPClipboardFiles::IsRecord(record) &&
|
||||||
|
CLGMPClipboardFiles::DirectionValid(record,
|
||||||
|
CLGMPClipboardFiles::Direction::HELPER_TO_CLIENT) &&
|
||||||
|
!m_files.TransferActive(record.transfer) &&
|
||||||
|
(record.type == KVMFR_CLIPBOARD_MESSAGE_FILE_ACQUIRE ||
|
||||||
|
record.type == KVMFR_CLIPBOARD_MESSAGE_FILE_REQUEST))
|
||||||
|
{
|
||||||
|
// The LGMP owner may disappear or replace its dataset before Helper
|
||||||
|
// observes the matching lifecycle record. Fail new work locally without
|
||||||
|
// tearing down the shared Helper clipboard channel.
|
||||||
|
QueueStaleFileRecord(record);
|
||||||
|
ClearOutboundBlock();
|
||||||
|
Wake();
|
||||||
|
return ClipboardChannelResult::ACCEPTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!validOutbound || (record.length && !data))
|
||||||
return ClipboardChannelResult::FAILED;
|
return ClipboardChannelResult::FAILED;
|
||||||
|
|
||||||
if (record.transfer &&
|
if (record.transfer &&
|
||||||
@@ -1248,6 +1398,7 @@ ClipboardChannelResult CLGMPClipboardTransport::SendClipboard(
|
|||||||
if (OwnerScopedLifecycle(record))
|
if (OwnerScopedLifecycle(record))
|
||||||
{
|
{
|
||||||
QueueStaleRequestCancel(record);
|
QueueStaleRequestCancel(record);
|
||||||
|
QueueStaleFileRecord(record);
|
||||||
ClearOutboundBlock();
|
ClearOutboundBlock();
|
||||||
Wake();
|
Wake();
|
||||||
return ClipboardChannelResult::ACCEPTED;
|
return ClipboardChannelResult::ACCEPTED;
|
||||||
@@ -1265,7 +1416,9 @@ ClipboardChannelResult CLGMPClipboardTransport::SendClipboard(
|
|||||||
}
|
}
|
||||||
|
|
||||||
ClipboardChannelResult result;
|
ClipboardChannelResult result;
|
||||||
if (record.type == KVMFR_CLIPBOARD_MESSAGE_DATA)
|
if (record.type == KVMFR_CLIPBOARD_MESSAGE_FILE_DATA)
|
||||||
|
result = SendData(record, data);
|
||||||
|
else if (record.type == KVMFR_CLIPBOARD_MESSAGE_DATA)
|
||||||
{
|
{
|
||||||
if (!m_helperToClient.Active() ||
|
if (!m_helperToClient.Active() ||
|
||||||
record.transfer != m_helperToClient.transfer)
|
record.transfer != m_helperToClient.transfer)
|
||||||
@@ -1316,6 +1469,7 @@ void CLGMPClipboardTransport::ClipboardState(
|
|||||||
ReleaseOwner("Helper endpoint changed", false);
|
ReleaseOwner("Helper endpoint changed", false);
|
||||||
m_clientToHelper.Clear();
|
m_clientToHelper.Clear();
|
||||||
m_helperToClient.Clear();
|
m_helperToClient.Clear();
|
||||||
|
m_files.Reset();
|
||||||
m_clientClipboardGeneration = 0;
|
m_clientClipboardGeneration = 0;
|
||||||
m_clientFormats = 0;
|
m_clientFormats = 0;
|
||||||
m_discardHelperToClient = 0;
|
m_discardHelperToClient = 0;
|
||||||
@@ -1344,6 +1498,7 @@ void CLGMPClipboardTransport::ClipboardReset(
|
|||||||
ReleaseOwner("Helper reset", false);
|
ReleaseOwner("Helper reset", false);
|
||||||
m_clientToHelper.Clear();
|
m_clientToHelper.Clear();
|
||||||
m_helperToClient.Clear();
|
m_helperToClient.Clear();
|
||||||
|
m_files.Reset();
|
||||||
m_clientClipboardGeneration = 0;
|
m_clientClipboardGeneration = 0;
|
||||||
m_clientFormats = 0;
|
m_clientFormats = 0;
|
||||||
m_discardHelperToClient = 0;
|
m_discardHelperToClient = 0;
|
||||||
@@ -1385,8 +1540,11 @@ void CLGMPClipboardTransport::Thread()
|
|||||||
else if (m_ownerClientID && GetTickCount64() >= m_ownerDeadline)
|
else if (m_ownerClientID && GetTickCount64() >= m_ownerDeadline)
|
||||||
ReleaseOwner("lease expired", true);
|
ReleaseOwner("lease expired", true);
|
||||||
|
|
||||||
|
// A restarted client can reuse dataset and transfer IDs. Deliver every
|
||||||
|
// old-owner cleanup record before admitting messages from a new owner.
|
||||||
if (!RetryTarget() || !PumpInternalTarget() ||
|
if (!RetryTarget() || !PumpInternalTarget() ||
|
||||||
!DrainMessage() || !PublishStatus() ||
|
(m_internalTargetCount == 0 && !DrainMessage()) ||
|
||||||
|
!PublishStatus() ||
|
||||||
!ReplayClipboard() || !PostGrants())
|
!ReplayClipboard() || !PostGrants())
|
||||||
{
|
{
|
||||||
notifyFailed = true;
|
notifyFailed = true;
|
||||||
@@ -1398,7 +1556,7 @@ void CLGMPClipboardTransport::Thread()
|
|||||||
notifyReady = m_target != nullptr;
|
notifyReady = m_target != nullptr;
|
||||||
readyTarget = m_target;
|
readyTarget = m_target;
|
||||||
}
|
}
|
||||||
if (m_pendingTarget.valid || m_ownerClientID)
|
if (m_pendingTarget.valid || m_internalTargetCount || m_ownerClientID)
|
||||||
timeout = ACTIVE_POLL_MS;
|
timeout = ACTIVE_POLL_MS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include "CSRWLock.h"
|
#include "CSRWLock.h"
|
||||||
#include "transport/IClipboardSource.h"
|
#include "transport/IClipboardSource.h"
|
||||||
|
#include "transport/lgmp/CLGMPClipboardFiles.h"
|
||||||
|
|
||||||
#include "common/KVMFRClipboard.h"
|
#include "common/KVMFRClipboard.h"
|
||||||
#include "common/LGMPConfig.h"
|
#include "common/LGMPConfig.h"
|
||||||
@@ -39,8 +40,10 @@ class CLGMPHost;
|
|||||||
class CLGMPClipboardTransport final : public IClipboardSource
|
class CLGMPClipboardTransport final : public IClipboardSource
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
static constexpr unsigned MEMORY_COUNT = 2;
|
static constexpr unsigned MEMORY_COUNT = 8;
|
||||||
static constexpr unsigned INTERNAL_TARGET_COUNT = 3;
|
static constexpr unsigned INTERNAL_TARGET_COUNT =
|
||||||
|
CLGMPClipboardFiles::MAX_ACQUISITIONS +
|
||||||
|
CLGMPClipboardFiles::MAX_REQUESTS + 3;
|
||||||
static constexpr ULONGLONG OWNER_LEASE_MS = 1000;
|
static constexpr ULONGLONG OWNER_LEASE_MS = 1000;
|
||||||
static constexpr DWORD ACTIVE_POLL_MS = 5;
|
static constexpr DWORD ACTIVE_POLL_MS = 5;
|
||||||
static constexpr DWORD IDLE_POLL_MS = 50;
|
static constexpr DWORD IDLE_POLL_MS = 50;
|
||||||
@@ -135,6 +138,7 @@ private:
|
|||||||
PendingTarget m_pendingTarget;
|
PendingTarget m_pendingTarget;
|
||||||
KVMFRClipboardMessage m_internalTarget[INTERNAL_TARGET_COUNT] = {};
|
KVMFRClipboardMessage m_internalTarget[INTERNAL_TARGET_COUNT] = {};
|
||||||
unsigned m_internalTargetCount = 0;
|
unsigned m_internalTargetCount = 0;
|
||||||
|
CLGMPClipboardFiles m_files;
|
||||||
|
|
||||||
bool Initialize();
|
bool Initialize();
|
||||||
void DeInit();
|
void DeInit();
|
||||||
@@ -156,6 +160,8 @@ private:
|
|||||||
void QueueTransferCancel(const Transfer& transfer);
|
void QueueTransferCancel(const Transfer& transfer);
|
||||||
void QueueStaleRequestCancel(
|
void QueueStaleRequestCancel(
|
||||||
const KVMFRClipboardMessage& request);
|
const KVMFRClipboardMessage& request);
|
||||||
|
void QueueStaleFileRecord(const KVMFRClipboardMessage& record);
|
||||||
|
void QueueFileDisconnect();
|
||||||
bool QueueInternalTarget(const KVMFRClipboardMessage& record);
|
bool QueueInternalTarget(const KVMFRClipboardMessage& record);
|
||||||
bool PumpInternalTarget();
|
bool PumpInternalTarget();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user