Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions experiments/shell/clipboard.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,29 @@ class ClipboardError : public std::runtime_error {
class ClipboardAccess {
public:
// A hosted desktop can hold the clipboard for a few milliseconds at a time
// (Explorer or a neighbouring test). Reads retry briefly so contention is
// not reported as a hard refusal; writes keep the immediate failure because
// the copy-failure contract asserts the first OpenClipboard result.
explicit ClipboardAccess(HWND owner, bool retry = false) {
constexpr int attempts = 5;
// (Explorer or a neighbouring test), which turns contention into a spurious
// hard refusal. Reads and writes both retry briefly on ERROR_ACCESS_DENIED.
// The copy-failure contract still holds: its negative fixture keeps the
// clipboard owned for the whole attempt window, so the failure and its
// "OpenClipboard: 5" status are unchanged, only reached a few ms later.
explicit ClipboardAccess(HWND owner, int attempts, DWORD first_delay_ms) {
for (int attempt = 0; attempt < attempts; ++attempt) {
if (OpenClipboard(owner)) return;
const DWORD error = GetLastError();
if (!retry || error != ERROR_ACCESS_DENIED || attempt + 1 == attempts)
if (error != ERROR_ACCESS_DENIED || attempt + 1 == attempts)
throw ClipboardError("OpenClipboard", error);
Sleep(static_cast<DWORD>(20 * (attempt + 1)));
Sleep(first_delay_ms * static_cast<DWORD>(attempt + 1));
}
throw ClipboardError("OpenClipboard", GetLastError());
}
explicit ClipboardAccess(HWND owner, bool retry = false) : ClipboardAccess(owner, retry ? 5 : 1, 20) {}
~ClipboardAccess() { CloseClipboard(); }
ClipboardAccess(const ClipboardAccess&) = delete;
};
inline std::u16string read_unicode_clipboard(HWND owner, size_t max_units = PastePolicy::hard_utf16_units) {
static_assert(sizeof(wchar_t) == sizeof(char16_t), "Windows UTF-16 expected");
if (max_units > PastePolicy::hard_utf16_units) throw std::length_error("clipboard limit exceeds hard bound");
ClipboardAccess clipboard(owner, true);
ClipboardAccess clipboard(owner, 5, 20);
if (!IsClipboardFormatAvailable(CF_UNICODETEXT)) throw ClipboardError("Unicode text unavailable", ERROR_NOT_FOUND);
HANDLE memory = GetClipboardData(CF_UNICODETEXT);
if (!memory) throw ClipboardError("GetClipboardData", GetLastError());
Expand All @@ -58,7 +60,9 @@ inline void write_unicode_clipboard(HWND owner, std::u16string_view text) {
if (!text.empty()) std::memcpy(destination, text.data(), text.size() * sizeof(char16_t));
destination[text.size()] = 0;
GlobalUnlock(memory);
ClipboardAccess clipboard(owner);
// Three short attempts (<=60 ms): enough for micro-contention, still far
// shorter than the owned hold the copy-failure contract relies on.
ClipboardAccess clipboard(owner, 3, 10);
if (!EmptyClipboard()) throw ClipboardError("EmptyClipboard", GetLastError());
if (!SetClipboardData(CF_UNICODETEXT, memory)) throw ClipboardError("SetClipboardData", GetLastError());
allocation.value = nullptr; // The window station owns the allocation now.
Expand Down
Loading