Skip to content

fix: respond to ESC[6n cursor position queries reactively#283

Merged
psmux merged 1 commit into
psmux:masterfrom
LizardLiang:fix/pwsh-omp-issue
May 8, 2026
Merged

fix: respond to ESC[6n cursor position queries reactively#283
psmux merged 1 commit into
psmux:masterfrom
LizardLiang:fix/pwsh-omp-issue

Conversation

@LizardLiang

@LizardLiang LizardLiang commented May 8, 2026

Copy link
Copy Markdown
Contributor

Closes #282

Problem

PowerShell (pwsh) freezes after Windows lock/unlock when running inside psmux. The root cause: pwsh emits ESC[6n (Cursor Position Request) at startup and again after session events like lock/unlock. The host must respond with ESC[row;colR or pwsh blocks indefinitely waiting for the response.

At spawn time, psmux already sends a preemptive ESC[1;1R to unblock the initial startup query. But this preemptive write is a one-shot — it cannot cover re-issued CPR queries after lock/unlock.

Solution

  • Add scan_cpr_query() in the reader thread to detect ESC[6n in PTY output
  • When detected, set a per-pane cpr_pending flag and a global CPR_DATA_PENDING atomic
  • In the server event loop (gated on data_ready), drain pending CPR flags and write the real ESC[row;colR response using the current cursor position from the VT parser
  • Retain the preemptive ESC[1;1R write at spawn time to cover the initial startup race window

Changes

  • src/types.rs — add cpr_pending: Arc<AtomicBool> to Pane and WarmPane; add CPR_DATA_PENDING global atomic
  • src/pane.rs — add scan_cpr_query(), thread cpr_pending through spawn_reader_thread (all 6 construction sites), add CPR signal in reader thread
  • src/server/helpers.rs — add drain_cpr_pending() tree walker
  • src/server/mod.rs — call drain_cpr_pending when CPR_DATA_PENDING is set
  • src/popup.rs / src/proxy_pane.rs — populate cpr_pending field (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 optimization

Test plan

  • cargo test cpr — all 10 tests pass
  • cargo build — clean
  • Lock Windows and unlock while pwsh is running in a psmux pane — verify pwsh remains responsive and prompt re-renders correctlys.

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.
@psmux psmux merged commit 8066250 into psmux:master May 8, 2026
3 checks passed
@psmux

psmux commented May 8, 2026

Copy link
Copy Markdown
Owner

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 bell_pending architecture and replicated it precisely for the CPR case. The two tier atomic signaling (cpr_pending per pane + CPR_DATA_PENDING global to skip the tree walk), the scan_cpr_query pre check optimization with data.contains(&0x1b), and the decision to keep the preemptive spawn time write alongside the new reactive path all show a deep understanding of how the server loop and reader threads interact.

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 helpers.rs is also welcome.

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!

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.

pwsh pane freezes when creating new window or split after lock/unlock

2 participants