[idd] clipboard: handle delayed-render sequence changes

This commit is contained in:
Geoffrey McRae
2026-08-25 22:22:11 +10:00
parent a1326af2f4
commit 057073924a
2 changed files with 108 additions and 26 deletions

View File

@@ -3404,7 +3404,8 @@ void CClipboardManager::HandleRequest(
record.clipboardGeneration != m_localGeneration || record.clipboardGeneration != m_localGeneration ||
record.clipboardGeneration != Atomic::Load( record.clipboardGeneration != Atomic::Load(
m_liveLocalGeneration, std::memory_order_acquire) || m_liveLocalGeneration, std::memory_order_acquire) ||
GetClipboardSequenceNumber() != m_localSequence) GetClipboardSequenceNumber() != m_localSequence ||
GetClipboardOwner() != m_localOwner)
{ {
QueueCancel(record, ERROR_NOT_FOUND); QueueCancel(record, ERROR_NOT_FOUND);
return; return;
@@ -3418,8 +3419,11 @@ void CClipboardManager::HandleRequest(
return; return;
} }
std::shared_ptr<CClipboardSpool> spool = const DWORD sequence = m_localSequence;
CaptureFormat(record.format, m_localSequence); const HWND owner = m_localOwner;
DWORD capturedSequence = sequence;
std::shared_ptr<CClipboardSpool> spool = CaptureFormat(
record.format, sequence, owner, capturedSequence);
if (!spool) if (!spool)
{ {
const DWORD error = GetLastError(); const DWORD error = GetLastError();
@@ -3428,6 +3432,38 @@ void CClipboardManager::HandleRequest(
return; return;
} }
bool generationLive = false;
bool transferLive = false;
{
std::lock_guard<std::mutex> lock(m_outgoingLock);
generationLive =
record.clipboardGeneration == Atomic::Load(
m_liveLocalGeneration, std::memory_order_acquire);
transferLive = generationLive &&
record.transfer == Atomic::Load(
m_outgoingTransfer, std::memory_order_acquire);
if (generationLive && capturedSequence != sequence)
{
m_localSequence = capturedSequence;
if (capturedSequence)
{
m_materializedSequence = capturedSequence;
m_materializedGeneration = record.clipboardGeneration;
}
else
{
m_materializedSequence = 0;
m_materializedGeneration = 0;
}
}
}
if (!transferLive)
{
ReleaseOutgoing(record.transfer);
QueueCancel(record, ERROR_OPERATION_ABORTED);
return;
}
Work data; Work data;
data.type = WorkType::SEND_DATA; data.type = WorkType::SEND_DATA;
data.record = record; data.record = record;
@@ -3970,6 +4006,23 @@ CClipboardManager::CaptureClipboardFiles(DWORD sequence, int rawFormatCount,
void CClipboardManager::HandleClipboardUpdate() void CClipboardManager::HandleClipboardUpdate()
{ {
const DWORD sequenceBefore = GetClipboardSequenceNumber();
const HWND owner = GetClipboardOwner();
const DWORD sequenceAfter = GetClipboardSequenceNumber();
// GetClipboardData can materialize delayed data and post an update without
// changing the clipboard that the current generation represents.
if (sequenceBefore &&
sequenceBefore == sequenceAfter &&
sequenceBefore == m_materializedSequence &&
owner == m_localOwner &&
m_materializedGeneration &&
m_materializedGeneration == Atomic::Load(
m_liveLocalGeneration, std::memory_order_acquire))
return;
m_materializedSequence = 0;
m_materializedGeneration = 0;
if (m_applyingRemote || IsOurClipboard()) if (m_applyingRemote || IsOurClipboard())
{ {
ClearLocalRetry(); ClearLocalRetry();
@@ -4025,16 +4078,21 @@ void CClipboardManager::PublishLocalClipboard()
Atomic::Store(m_liveLocalGeneration, UINT64_C(0), Atomic::Store(m_liveLocalGeneration, UINT64_C(0),
std::memory_order_release); std::memory_order_release);
m_localSequence = GetClipboardSequenceNumber(); m_localSequence = GetClipboardSequenceNumber();
m_localOwner = GetClipboardOwner();
m_materializedSequence = 0;
m_materializedGeneration = 0;
return; return;
} }
const DWORD before = GetClipboardSequenceNumber(); const DWORD before = GetClipboardSequenceNumber();
const HWND ownerBefore = GetClipboardOwner();
uint32_t formats = EnumerateFormats(); uint32_t formats = EnumerateFormats();
const uint32_t recognizedFormats = formats; const uint32_t recognizedFormats = formats;
const int rawFormatCount = CountClipboardFormatsLogged( const int rawFormatCount = CountClipboardFormatsLogged(
"PublishLocalClipboard", before); "PublishLocalClipboard", before);
const DWORD after = GetClipboardSequenceNumber(); const DWORD after = GetClipboardSequenceNumber();
if (before != after) const HWND ownerAfter = GetClipboardOwner();
if (before != after || ownerBefore != ownerAfter)
{ {
ClearLocalRetry(); ClearLocalRetry();
PostMessageW(m_hwnd, WM_CLIPBOARDUPDATE, 0, 0); PostMessageW(m_hwnd, WM_CLIPBOARDUPDATE, 0, 0);
@@ -4050,7 +4108,8 @@ void CClipboardManager::PublishLocalClipboard()
after, rawFormatCount, fileViaOLE, fileRetryStage, fileError, after, rawFormatCount, fileViaOLE, fileRetryStage, fileError,
fileOleError); fileOleError);
const DWORD capturedSequence = GetClipboardSequenceNumber(); const DWORD capturedSequence = GetClipboardSequenceNumber();
if (capturedSequence != after) const HWND capturedOwner = GetClipboardOwner();
if (capturedSequence != after || capturedOwner != ownerAfter)
{ {
DEBUG_ERROR_HR(HRESULT_FROM_WIN32(ERROR_RETRY), DEBUG_ERROR_HR(HRESULT_FROM_WIN32(ERROR_RETRY),
"Failed to publish local clipboard: " "Failed to publish local clipboard: "
@@ -4072,6 +4131,9 @@ void CClipboardManager::PublishLocalClipboard()
if (!generation) if (!generation)
generation = ++m_localGeneration; generation = ++m_localGeneration;
m_localSequence = after; m_localSequence = after;
m_localOwner = ownerAfter;
m_materializedSequence = 0;
m_materializedGeneration = 0;
if (files) if (files)
{ {
formats |= KVMFR_CLIPBOARD_FORMAT_MASK_FILES; formats |= KVMFR_CLIPBOARD_FORMAT_MASK_FILES;
@@ -4183,6 +4245,9 @@ void CClipboardManager::InvalidateOutgoing(uint32_t reason)
void CClipboardManager::InvalidateLocalClipboard(uint32_t reason) void CClipboardManager::InvalidateLocalClipboard(uint32_t reason)
{ {
m_localSequence = 0; m_localSequence = 0;
m_localOwner = nullptr;
m_materializedSequence = 0;
m_materializedGeneration = 0;
RetireLocalFileDataset(); RetireLocalFileDataset();
InvalidateOutgoing(reason); InvalidateOutgoing(reason);
} }
@@ -4237,6 +4302,9 @@ void CClipboardManager::ExpireLocalRetry(DWORD sequence)
if (!generation) if (!generation)
generation = ++m_localGeneration; generation = ++m_localGeneration;
m_localSequence = sequence; m_localSequence = sequence;
m_localOwner = GetClipboardOwner();
m_materializedSequence = 0;
m_materializedGeneration = 0;
PublishClear(generation); PublishClear(generation);
} }
@@ -4472,9 +4540,12 @@ UINT CClipboardManager::ToWindowsFormat(KVMFRClipboardFormat format) const
} }
std::shared_ptr<CClipboardSpool> CClipboardManager::CaptureFormat( std::shared_ptr<CClipboardSpool> CClipboardManager::CaptureFormat(
KVMFRClipboardFormat format, DWORD sequence) KVMFRClipboardFormat format, DWORD sequence, HWND owner,
DWORD& capturedSequence)
{ {
if (GetClipboardSequenceNumber() != sequence) capturedSequence = sequence;
if (GetClipboardSequenceNumber() != sequence ||
GetClipboardOwner() != owner)
{ {
SetLastError(ERROR_RETRY); SetLastError(ERROR_RETRY);
return nullptr; return nullptr;
@@ -4488,7 +4559,8 @@ std::shared_ptr<CClipboardSpool> CClipboardManager::CaptureFormat(
return nullptr; return nullptr;
} }
if (GetClipboardSequenceNumber() != sequence) if (GetClipboardSequenceNumber() != sequence ||
GetClipboardOwner() != owner)
{ {
CloseClipboard(); CloseClipboard();
SetLastError(ERROR_RETRY); SetLastError(ERROR_RETRY);
@@ -4635,7 +4707,12 @@ std::shared_ptr<CClipboardSpool> CClipboardManager::CaptureFormat(
if (!success && !error) if (!success && !error)
error = ERROR_NOT_ENOUGH_MEMORY; error = ERROR_NOT_ENOUGH_MEMORY;
GlobalUnlock(handle); GlobalUnlock(handle);
if (GetClipboardSequenceNumber() != sequence) const DWORD renderedSequence = GetClipboardSequenceNumber();
const HWND renderedOwner = GetClipboardOwner();
const bool sequenceChanged = renderedSequence != sequence;
// With the clipboard still open, another process cannot replace it. A
// sequence advance from the same owner is delayed or synthesized rendering.
if (renderedOwner != owner || (sequenceChanged && !owner))
{ {
CloseClipboard(); CloseClipboard();
SetLastError(ERROR_RETRY); SetLastError(ERROR_RETRY);
@@ -4647,6 +4724,7 @@ std::shared_ptr<CClipboardSpool> CClipboardManager::CaptureFormat(
SetLastError(error); SetLastError(error);
return nullptr; return nullptr;
} }
capturedSequence = renderedSequence;
return spool; return spool;
} }

View File

@@ -225,6 +225,9 @@ private:
uint64_t m_epoch = 0; uint64_t m_epoch = 0;
uint64_t m_localGeneration = 0; uint64_t m_localGeneration = 0;
DWORD m_localSequence = 0; DWORD m_localSequence = 0;
HWND m_localOwner = nullptr;
DWORD m_materializedSequence = 0;
uint64_t m_materializedGeneration = 0;
DWORD m_localRetrySequence = 0; DWORD m_localRetrySequence = 0;
uint64_t m_localRetryDeadline = 0; uint64_t m_localRetryDeadline = 0;
uint64_t m_remoteGeneration = 0; uint64_t m_remoteGeneration = 0;
@@ -321,7 +324,8 @@ private:
KVMFRClipboardFormat ToWireFormat(UINT format) const; KVMFRClipboardFormat ToWireFormat(UINT format) const;
UINT ToWindowsFormat(KVMFRClipboardFormat format) const; UINT ToWindowsFormat(KVMFRClipboardFormat format) const;
std::shared_ptr<CClipboardSpool> CaptureFormat( std::shared_ptr<CClipboardSpool> CaptureFormat(
KVMFRClipboardFormat format, DWORD sequence); KVMFRClipboardFormat format, DWORD sequence, HWND owner,
DWORD& capturedSequence);
bool MaterializeFormat(KVMFRClipboardFormat format, bool MaterializeFormat(KVMFRClipboardFormat format,
UINT windowsFormat, CClipboardSpool& spool); UINT windowsFormat, CClipboardSpool& spool);