fix(input): prevent double Ctrl+letter delivery; use raw byte only for Ctrl+C#333
Conversation
…Enter/ESC/Ctrl - Replace send_modified_enter_event calls with send_modified_key_event(pid, '\r', ...) - Switch Ctrl+letter injection from Win32 VT escape sequences to WriteConsoleInputW to avoid corrupting ConPTY's VT parser (ESC sequences left parser buffering \x1b) - Fix char_to_vk to handle '\x1b' and '\r' explicitly since VkKeyScanW returns -1 for non-printable chars; reuse char_to_vk/vk_to_scan in send_modified_key_event - Add tests for char_to_vk covering ESC, carriage return, and alphabetic chars
There was a problem hiding this comment.
Pull request overview
Fixes Windows input regressions introduced by #329 by ensuring Ctrl+letter keystrokes are delivered exactly once, while preserving Ctrl+C’s ability to generate CTRL_C_EVENT for interrupting console processes.
Changes:
- Switch Ctrl+letter handling to “
WriteConsoleInputWfirst, raw-byte fallback” to prevent double-delivery. - Special-case Ctrl+C to always use the raw control byte path so ConPTY can generate
CTRL_C_EVENT. - Apply the same delivery strategy to both live key forwarding (including sync-input fanout) and the
send-keys C-xpath.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
hey any update on this issue? i just got psmux and is having the same issue. it seems the @psmux haven't fix it yet. should i just build this from source with @LizardLiang folk? many thanks |
|
hey hum i got the lastest version which has try to fix this issue (#329 ) but it's stil broken right now. |
I'm no longer able to reproduce this on this branch. Could you share the steps you used to trigger it? That'll help me confirm whether it's actually fixed or just not surfacing in my setup. |
|
hey @LizardLiang : i get psmux from winget (verison 3.3.5), make a .psmux.conf like this: then restart and install plugins. everything is normal. i then start a new session and re-name it, open 2 windows, rename the first one and open nvim on the second one and start editing a file. then as im trying to escape by to normal mode, it just stuck there. i can still see the plugin is working normally and i can still switch windows. the first window also preforms well. I have also try to open a new session and edit the same file in neovim and as soon as i press esc, it just suck there, no response to that window, but i can still use prefix and all other key blind. what's even wired is that when i open neovim on test file, i can type and esc normally. but when i try to improt a model (this is python) it just stuck. it might have to do with my neovim config, which you can check here I am using windows terminal 1.24.11321.0 on pwsh.exe. many thanks |
Just to make sure we're on the same page — have you tried building from source with the commit from #333? I've gone through your steps and used your psmux config, but the issue still isn't reproducing for me. |
|
hi i have build psmux from source with the latest commit and it seems to work. however, when i open a new window with prefix + c, i cannot type on it. the key blind still work tho. this is the only issue ive counter after building psmux |
|
Hey @LizardLiang and @ryannewcomer, thanks both for the diagnosis and the back-and-forth on this thread. Confirming what @LizardLiang already saw on his side: master is fine on both fronts. Commit 6bd8ec4 (#338) reshaped the Ctrl-letter path on master to branch on Ctrl+C — Ctrl+C goes through
The reason ryannewcomer hit it on 3.3.5 is that the winget build is from the v3.3.5 tag which is BEFORE 6bd8ec4 landed; the next release will pick it up. Closing this PR since the bug is already gone on master, with credit to your diagnosis, @LizardLiang — your write-up of the two regressions was what made it easy to verify. @ryannewcomer the "new window from prefix+c cannot accept typed input but keybindings still work" you described after building from source is a different bug; could you file a fresh issue with the exact steps + output of |

Hotfix for two regressions introduced by #329.
Regression 1 — Ctrl+letter triggers twice
Problem
Any Ctrl+letter keypress (e.g.
<C-w>in Neovim) fires twice — one press causes two actions.Root cause
#329 switched Ctrl+letter injection from Win32 VT sequences to two simultaneous delivery paths: writing the raw C0 control byte to the ConPTY pipe AND injecting a
KEY_EVENTviaWriteConsoleInputW. Both paths land in the child's console input buffer. ConPTY converts the raw byte into aKEY_EVENT, andWriteConsoleInputWinjects a second one — the child sees the keypress twice.Fix
Mirror the existing Alt+key pattern: try
WriteConsoleInputWfirst; write the raw byte only if injection fails. Exactly one delivery fires per keypress.Regression 2 — Ctrl+C cannot stop processes
Problem
Pressing
<C-c>no longer interrupts running processes such asping.Root cause
WriteConsoleInputWputs aKEY_EVENTrecord into the input queue. It does not triggerCTRL_C_EVENT. Process termination for CLI tools depends onCTRL_C_EVENT, which only ConPTY generates, and only from the raw byte path whenENABLE_PROCESSED_INPUTis set on the child's console. With both fixes from Regression 1 applied,'c'now also goes throughWriteConsoleInputW— soCTRL_C_EVENTis never generated andpingcannot be interrupted.Fix
'c'is excluded from theWriteConsoleInputWbranch and always falls through to the raw byte path. There is no single Windows API that delivers both aKEY_EVENTand aCTRL_C_EVENT; they are separate console mechanisms. The raw byte delegates the decision to ConPTY:CTRL_C_EVENTwhenENABLE_PROCESSED_INPUTis on (shells, CLI tools),KEY_EVENTwhen it is off (Neovim raw mode) — the same adaptive behaviour as a real keyboard.Test plan
<C-w>in Neovim triggers exactly once per keypressping -tstops on Ctrl+CChanges
src/input.rsWriteConsoleInputW-first, raw-byte-fallback pattern for all Ctrl+letters; exclude'c'fromWriteConsoleInputWbranch