fix: respond to ESC[6n cursor position queries reactively#283
Conversation
pwsh emits ESC[6n (CPR) at startup and again after session events such as Win+L / unlock. The single preemptive ESC[1;1R written at spawn time is long gone by then, leaving pwsh blocked indefinitely. Add a reactive responder: the parser thread scans each byte batch for ESC[6n via scan_cpr_query and raises cpr_pending + CPR_DATA_PENDING; the server loop drains these on every data_ready tick via drain_cpr_pending, which writes ESC[row;colR to the pane's PTY input. The preemptive write is kept — it still covers the spawn-time race where the query arrives before the first server-loop tick fires. CPR_DATA_PENDING global (mirrors PTY_DATA_READY) lets the server loop skip the tree walk entirely on ticks with no pending CPR. Includes 10 regression tests covering scan_cpr_query detection and CPR response format correctness.
|
Hey @LizardLiang, this is excellent work. Merged in 8066250. I reviewed the entire diff and I want to call out how clean this contribution is. You clearly studied the existing The 10 regression tests are thorough, covering detection, false positives, partial sequences, and the 0 based to 1 based coordinate conversion. The cosmetic reformatting in Really appreciate you taking the time to diagnose the root cause (pwsh re issuing DSR after lock/unlock) rather than just patching the symptom. This has been a pain point for users and your fix addresses it properly. Thank you for this contribution! |
Closes #282
Problem
PowerShell (
pwsh) freezes after Windows lock/unlock when running inside psmux. The root cause: pwsh emitsESC[6n(Cursor Position Request) at startup and again after session events like lock/unlock. The host must respond withESC[row;colRor pwsh blocks indefinitely waiting for the response.At spawn time, psmux already sends a preemptive
ESC[1;1Rto unblock the initial startup query. But this preemptive write is a one-shot — it cannot cover re-issued CPR queries after lock/unlock.Solution
scan_cpr_query()in the reader thread to detectESC[6nin PTY outputcpr_pendingflag and a globalCPR_DATA_PENDINGatomicdata_ready), drain pending CPR flags and write the realESC[row;colRresponse using the current cursor position from the VT parserESC[1;1Rwrite at spawn time to cover the initial startup race windowChanges
src/types.rs— addcpr_pending: Arc<AtomicBool>toPaneandWarmPane; addCPR_DATA_PENDINGglobal atomicsrc/pane.rs— addscan_cpr_query(), threadcpr_pendingthroughspawn_reader_thread(all 6 construction sites), add CPR signal in reader threadsrc/server/helpers.rs— adddrain_cpr_pending()tree walkersrc/server/mod.rs— calldrain_cpr_pendingwhenCPR_DATA_PENDINGis setsrc/popup.rs/src/proxy_pane.rs— populatecpr_pendingfield (popup's inline reader doesn't detect CPR; proxy forwards over TCP)tests-rs/test_cpr_responder.rs— 10 unit tests covering detection, false-positives, response format, and the 0x1b pre-check optimizationTest plan
cargo test cpr— all 10 tests passcargo build— clean