Fix text injection landing nowhere: post to cgAnnotatedSessionEventTap - #27
Closed
imtamiliniyan wants to merge 1 commit into
Closed
imtamiliniyan wants to merge 1 commit into
imtamiliniyan wants to merge 1 commit into
Conversation
TextInjector posted synthetic keystrokes to .cgSessionEventTap, the same event-pipeline location HotkeyMonitor installs its listen-only tap at (.headInsertEventTap on .cgSessionEventTap). The synthetic keyDown/keyUp events collided with that tap and were silently dropped before reaching the focused application - transcription and the fn-hotkey worked fine, but nothing ever got typed anywhere. Confirmed empirically with a standalone harness posting the same event via all three CGEventTapLocation options: cghidEventTap and cgSessionEventTap both no-op, cgAnnotatedSessionEventTap reliably lands in the focused text field. Related upstream: humanitas-labs#13, humanitas-labs#17
FernandoGomes83
added a commit
to FernandoGomes83/parrot
that referenced
this pull request
Aug 26, 2026
…e-unicode fallback path Post synthetic keystrokes to cgAnnotatedSessionEventTap so our own hotkey tap doesn't swallow them, and stop setting the unicode string on the key-up event, which duplicated the text in some apps. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJSwEJLoPGF23gRToanmHJ
7 tasks
Contributor
|
Thanks for this. A lot of the open PRs overlap, so rather than merging them one at a time I'm rewriting the fixes against a single plan. Your work is credited in #38, which now tracks the scope, and the commits that build on it will reference this PR. Closing in favour of that issue. |
birhantprkc
pushed a commit
to birhantprkc/parrot
that referenced
this pull request
Sep 28, 2026
TextInjector put the Unicode payload on both key-down and key-up, so some apps inserted every chunk twice. Terminals and Electron apps ignore CGEventKeyboardSetUnicodeString, so dictation there produced nothing and no error. Events had a nil source and inherited whatever modifiers the user held, so a held Control could turn text into shortcuts. And the injector never checked focus: a click during transcription sent the text to the wrong field, possibly a password field. Paste is now the default: the injector snapshots every representation of every pasteboard item, writes the transcript marked with org.nspasteboard.TransientType and ConcealedType so clipboard managers skip it, posts ⌘V, and restores the snapshot after 250 ms. A second paste inside that window keeps the first snapshot, so the user's own clipboard always comes back, and a copy made by anything else during the window is kept rather than overwritten. Paste works everywhere paste works, including the terminals and editors where type-unicode drops text silently, which is worth borrowing the clipboard for 250 ms. --inject-mode type-unicode keeps the old path for anyone who wants the clipboard left alone, now with the payload on key-down only. Every synthesized event comes from a private CGEventSource with explicit flags. Events still post at cgSessionEventTap. humanitas-labs#27 reported that location dropping events and cgAnnotatedSessionEventTap working, but our tap is listen-only and cannot swallow anything, so that explanation does not hold and there is no confirmed reason to move. The location is one constant, to be confirmed against the app matrix on hardware. At recording start the controller takes a FocusSnapshot: the frontmost app, its focused element, and whether that element is a secure text field or reports protected content. Before delivery a pure decision compares it with a fresh snapshot. A secure field at either end discards the transcript, neither typed nor copied, and logs why. A different app or element puts the transcript on the clipboard instead, and the overlay says so through a UserFacingError. The decision and the pasteboard session are tested, the latter also against a real private pasteboard. Thanks to humanitas-labs#24 (@justinhnaylor) for the key-down-only fix; humanitas-labs#27 (@imtamiliniyan) for the report that injection lands nowhere and the harness comparing tap locations; humanitas-labs#4 (@Entrepenulian) for paste mode with a full multi-representation snapshot, the guard against overlapping restores, ⌘V on keycode 9 with explicit flags, and the case for paste as the default; humanitas-labs#26 (@willmather95) for the secure-field check, the focus snapshot with a clipboard fallback, and the private event source; and @raffanegro for reporting dropped injections. Fixes humanitas-labs#38
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TextInjectorposts synthetic keystrokes to.cgSessionEventTap— the same event-pipeline location whereHotkeyMonitorinstalls its own listen-only tap (.headInsertEventTapon.cgSessionEventTap) to detect thefnhotkey. The injected keyDown/keyUp events collide with that tap and get silently dropped before ever reaching the focused application. Transcription and hotkey detection both work fine — nothing ever gets typed anywhere, in any app, regardless of focus.Confirmed empirically with a standalone harness posting the same event via all three
CGEventTapLocationoptions:cghidEventTapandcgSessionEventTapboth silently no-op;cgAnnotatedSessionEventTapreliably lands in the focused text field.Fixes/relates to #13, #17.