Skip to content

fix: harden Windows clipboard persistence and reject paste-conflict hotkeys#28

Closed
ShunmeiCho wants to merge 1 commit intomainfrom
fix/issue-27-windows-paste
Closed

fix: harden Windows clipboard persistence and reject paste-conflict hotkeys#28
ShunmeiCho wants to merge 1 commit intomainfrom
fix/issue-27-windows-paste

Conversation

@ShunmeiCho
Copy link
Copy Markdown
Owner

Closes #27 once verified on a Windows host.

Summary

Two bugs on Windows prevented the hotkey paste flow from working. This PR addresses both.

Bug 2 (confirmed deterministic fix)

Configuring the hotkey to ctrl+shift+v made paste silently fail:

  • windowsSendCtrlShiftV synthesizes Ctrl+Shift+V via SendKeys.
  • That synthesized keystroke is intercepted again by our own RegisterHotKey loop, and the hotkeyRunning re-entry guard swallows it.
  • Plain ctrl+v would also hijack the system paste shortcut.

parseHotkey now rejects ctrl+v and ctrl+shift+v with explanatory error messages (regardless of casing or modifier order), and saveHotkeyConfig inherits the validation. The default alt+shift+v is unaffected.

Bug 1 (persistence hardening, needs Windows smoke verification)

Clipboard data vanished between windowsSetClipboardText and windowsSendCtrlShiftV:

  • Set-Clipboard / Clipboard.SetText give clipboard ownership to an internal window inside the short-lived PowerShell process.
  • When the process exits, Windows destroys the window and empties the clipboard.

The fix adopts explicit persistence semantics rather than asserting a single root cause:

  1. Use Clipboard.SetDataObject(data, $true) — WinForms' documented "leave the data on the clipboard after this app exits" contract.
  2. Call OleFlushClipboard() as a belt-and-braces commit, because the exact persistence path for delayed-render formats (notably Bitmap) depends on Windows version and format. Using both keeps the fix robust without relying on any single API.

The same treatment is applied to windowsSetClipboardImage (used for the optional --restore-clipboard flow).

Tests

Added table-driven tests in cmd/cc-clip/hotkey_windows_test.go:

  • TestParseHotkeyAccepts — default, casing, extra modifiers, function keys
  • TestParseHotkeyRejectsPasteConflicts — ctrl+v / ctrl+shift+v across casings and modifier orderings
  • TestParseHotkeyNonVKeyNotRejected — scope check: the rule must only target the V key
  • TestSaveHotkeyConfigRejectsPasteConflicts — end-to-end rejection at the config layer

Verification performed locally

  • make test — full suite passes on darwin
  • make vet — clean
  • GOOS=windows GOARCH=amd64 go build ./cmd/cc-clip — passes
  • GOOS=windows GOARCH=arm64 go build ./cmd/cc-clip — passes
  • GOOS=windows GOARCH=386 go build ./cmd/cc-clip — passes
  • GOOS=windows GOARCH=amd64 go vet ./cmd/cc-clip — clean
  • GOOS=windows GOARCH=amd64 go test -c ./cmd/cc-clip — Windows-tagged tests compile cleanly

Verification still needed (Windows host)

I do not have a Windows host, so the runtime behavior of the clipboard persistence change must be confirmed by someone who does. Requested smoke checks:

  • cc-clip send --paste on Windows 11 with Git Bash / Windows Terminal: image uploads and remote path is actually pasted into the terminal
  • cc-clip hotkey --run-loop with default alt+shift+v: copy image → hotkey → path pasted
  • Configure hotkey to ctrl+shift+v via the config path: rejected with a clear error message
  • Configure hotkey to ctrl+v: rejected with a clear error message
  • With --restore-clipboard, the original image is back on the clipboard after paste

@nucleoid — would you be willing to test this branch on your Windows 11 setup? Binaries can be built from this branch, or I can attach prebuilt .exe artifacts if you prefer.

Files touched

  • cmd/cc-clip/hotkey_windows.go — conflict validation in parseHotkey
  • cmd/cc-clip/hotkey_windows_test.go — new tests
  • cmd/cc-clip/send_windows.goSetDataObject(..., $true) + OleFlushClipboard wrapper, applied to both text and image setters

Refs #27.

…otkeys (#27)

Two bugs on Windows prevented the hotkey paste flow from working:

Bug 1 — clipboard data vanished after `windowsSetClipboardText`. The
short-lived PowerShell process that called `Set-Clipboard` owned the
invisible clipboard window; when it exited, Windows destroyed the
window and emptied the clipboard before `windowsSendCtrlShiftV`
could fire. Replace `Set-Clipboard` and `Clipboard.SetImage` with
`Clipboard.SetDataObject(..., $true)` (WinForms' explicit
"persist after exit" contract) and additionally call
`OleFlushClipboard()` as a belt-and-braces commit for delayed-render
formats. Needs Windows smoke verification by the reporter; the fix
adopts persistence semantics rather than claiming a definitive root
cause.

Bug 2 — configuring the hotkey to `ctrl+shift+v` made paste silently
fail. `windowsSendCtrlShiftV` synthesizes the same keystroke, which
is re-caught by our own `RegisterHotKey` loop (the `hotkeyRunning`
guard swallows it). Plain `ctrl+v` would also hijack the system paste
shortcut. Reject both combinations in `parseHotkey`, with error
messages that explain the conflict.

Tests: table-driven coverage for accepted bindings, rejection of the
two conflict combinations across casings/orderings, a non-V key
sanity case, and end-to-end rejection via `saveHotkeyConfig`.

Refs: #27
@ShunmeiCho
Copy link
Copy Markdown
Owner Author

Closing in favor of a split approach to #27:

This PR combined both portions in a single commit and description, which would confuse reviewers once the hotkey half was already in main. #30 has a clean diff of just send_windows.go and a description that no longer needs to disclaim the split.

Nothing is lost; no force-push was used. The branch behind this PR (fix/issue-27-windows-paste) will be deleted.

@ShunmeiCho ShunmeiCho closed this Apr 21, 2026
@ShunmeiCho ShunmeiCho deleted the fix/issue-27-windows-paste branch April 21, 2026 15:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows: clipboard empty after paste + hotkey conflicts with paste keystroke

1 participant