fix: inject text for control-socket send_key so keys reach the PTY - #112
fix: inject text for control-socket send_key so keys reach the PTY#112calebcarvalho wants to merge 2 commits into
Conversation
bvolpato
left a comment
There was a problem hiding this comment.
Blocking: rust/limux-host-linux/src/terminal.rs:183-211 still sends keycode: 0. Ghostty derives physical key only from keycode, so this fixes printable input and four hardcoded controls but leaves arrows, function keys, and physical keybindings unidentified. send-key Up, for example, still has neither text nor physical key and cannot produce its PTY sequence.
Map keyval to display keycode as #114 does, then verify Return plus one non-text key end to end. Socket-only scope and CString lifetime look good.
nonblocking: no regression coverage or CI result currently exercises non-text keys or terminal keyboard modes.
(Review assisted by gpt-5.6-sol)
Addresses @bvolpato's review. He is right, and the fix was incomplete. A socket-injected key needs BOTH halves of the ghostty key event, and supplying only one drops it silently: keycode only -> `limux send-key a` returns OK and writes nothing text only -> Enter, arrows, F-keys and ctrl-chords cannot be encoded The original commit fixed the first half (keycode 0 meant ghostty saw an unidentified physical key and dropped the event), which made Enter and arrows work and closed the four-verb loop. But `translate_key_event` still left `text` null, so ordinary printable input had nothing to write and vanished exactly as before. It went unnoticed because orchestration uses `send` for text and `send-key` only for Enter. The codebase already says so, 550 lines above the bug, on the GTK key controller: // Send key events with the text field populated. Ghostty uses the // text field for actual character input and the keycode for bindings. So: mirror the GTK path. Populate `press.text` from the existing `key_event_text` helper, keeping the CString alive across `ghostty_surface_key`; leave the release event textless, as the GTK controller does. The GTK path sources this text from the input method -- a socket-injected key has no IM behind it, so derive it the same way GTK's own fallback does. `key_event_text` returns None for control characters, and that is load-bearing rather than incidental: Enter and ctrl-chords must be *encoded* by ghostty from key + mods, not written as literal bytes. Writing "\r" as text instead of letting ghostty encode Return is how you break the kitty keyboard protocol. This makes am-will#112 and this PR complementary rather than alternatives -- am-will#112 supplies the text half, this supplies the physical-key half, and both are required. Verified live on Wayland, all four classes at once, because fixing one by breaking another is the obvious failure mode here: printable `send-key e,c,h,o,space,h,i` -> `echo hi` appears at the prompt Enter `send-key enter` -> it executes (once) arrows `send-key up` then enter -> history recalled; it executes twice ctrl-chord `send-key '<ctrl>c'` -> ^C interrupts a running `sleep 60` Regression test pins the contract for all three key classes.
bvolpato
left a comment
There was a problem hiding this comment.
Current quality check is green, but current head still has two functional blockers:
terminal.rs:183-198sendskeycode: 0. Ghostty resolves physical key only from keycode, so arrows, function keys, and physical bindings remain unidentified even though printable text and four hardcoded controls work.main.rs:763-765uses shellechofor arbitrary--shell-safepayloads.dashandzshinterpret backslash escapes; payload containing\cis truncated and\tis changed. Use quotedprintf '%s\n'or equivalent byte-preserving command.
Latest shell-safe tests only assert generated source string, so they miss target-shell interpretation. Please map keyval to display keycode and test shell output with escape-bearing payload.
(assisted by gpt-5.6-sol)
Addresses @bvolpato's review. He is right, and the fix was incomplete. A socket-injected key needs BOTH halves of the ghostty key event, and supplying only one drops it silently: keycode only -> `limux send-key a` returns OK and writes nothing text only -> Enter, arrows, F-keys and ctrl-chords cannot be encoded The original commit fixed the first half (keycode 0 meant ghostty saw an unidentified physical key and dropped the event), which made Enter and arrows work and closed the four-verb loop. But `translate_key_event` still left `text` null, so ordinary printable input had nothing to write and vanished exactly as before. It went unnoticed because orchestration uses `send` for text and `send-key` only for Enter. The codebase already says so, 550 lines above the bug, on the GTK key controller: // Send key events with the text field populated. Ghostty uses the // text field for actual character input and the keycode for bindings. So: mirror the GTK path. Populate `press.text` from the existing `key_event_text` helper, keeping the CString alive across `ghostty_surface_key`; leave the release event textless, as the GTK controller does. The GTK path sources this text from the input method -- a socket-injected key has no IM behind it, so derive it the same way GTK's own fallback does. `key_event_text` returns None for control characters, and that is load-bearing rather than incidental: Enter and ctrl-chords must be *encoded* by ghostty from key + mods, not written as literal bytes. Writing "\r" as text instead of letting ghostty encode Return is how you break the kitty keyboard protocol. This makes am-will#112 and this PR complementary rather than alternatives -- am-will#112 supplies the text half, this supplies the physical-key half, and both are required. Verified live on Wayland, all four classes at once, because fixing one by breaking another is the obvious failure mode here: printable `send-key e,c,h,o,space,h,i` -> `echo hi` appears at the prompt Enter `send-key enter` -> it executes (once) arrows `send-key up` then enter -> history recalled; it executes twice ctrl-chord `send-key '<ctrl>c'` -> ^C interrupts a running `sleep 60` Regression test pins the contract for all three key classes.
Summary
send-keyover the control socket is silently ignored — no key (not even aprintable character, and critically not
Return) reaches the shell. This makessocket-driven automation unusable on Linux: you can build layouts and
sendtext into a pane, but you can never submit a command.
Root cause
TerminalHandle::send_keybuilds itsghostty_input_key_sviatranslate_key_event, which hard-codes:Ghostty relies on the
textfield to write the character to the PTY. Becausesend_keynever populates it,ghostty_surface_keyreceives a key event withno text and nothing is written to the shell.
key_event_textcan't help hereeither — it deliberately returns
Nonefor control characters, soReturn(
\r) would be dropped even if it were consulted.Repro on
main:Fix
Populate
press.textinsend_keyfrom a small helper that maps the commoncontrol keys to their PTY bytes (
Return/KP_Enter→\r,Tab→\t,Escape→\x1b,BackSpace→\x7f) and falls back tokey_event_textfor printable characters. Only the control-socket path is touched; interactive
GTK key handling is unchanged.
Test plan
limux send-key --surface <ref> Returnon a bare bash prompt now submitsthe buffered command (verified end-to-end: the command executes and its
output appears, followed by a fresh prompt).
send_key).cargo build --releaseis clean.Notes
This is the same underlying issue as the
send-key Returnportion of #108(which was closed unmerged); this PR is scoped to just the input fix and
extends it to the other common control keys.