Skip to content

fix(vt): send SS3 for Home and End under DECCKM - #960

Open
midagedev wants to merge 1 commit into
charmbracelet:mainfrom
midagedev:fix/vt-home-end-decckm
Open

fix(vt): send SS3 for Home and End under DECCKM#960
midagedev wants to merge 1 commit into
charmbracelet:mainfrom
midagedev:fix/vt-home-end-decckm

Conversation

@midagedev

Copy link
Copy Markdown

SendKey computes ack from ansi.ModeCursorKeys and uses it to pick SS3 or CSI for the four arrow keys (vt/key.go L124-147), but KeyHome and KeyEnd twenty lines below emit CSI unconditionally (L153-156). Under DECCKM they should be ESC O H and ESC O F.

Those are the forms this stack already recognizes on the way in. ultraviolet's key table lists them under its own // Application Cursor Key Mode (DECCKM) heading, right beside ESC O A..ESC O D:

// Application Cursor Key Mode (DECCKM)
"\x1bOA": {Code: KeyUp},
...
"\x1bOF": {Code: KeyEnd},
"\x1bOH": {Code: KeyHome},

So an Emulator whose output is decoded by ultraviolet round-trips the arrows and drops Home and End. terminfo says the same thing from the other side — xterm-256color pairs smkx=\E[?1h (which is DECCKM) with khome=\EOH and kend=\EOF, so an ncurses application that called keypad(true) compares against the SS3 forms and never matches the CSI ones.

How this has been tested

vt/key_test.go (new) covers all six cursor keys in both modes, so the four arrows document the behaviour Home and End now match.

A/B on this branch, against its parent 96af6d2:

  • parent + the new test: two failures, and only those two —
    --- FAIL: TestSendKeyCursorKeysFollowDECCKM/home
        key_test.go:60: DECCKM set: got "\x1b[H", want "\x1bOH"
    --- FAIL: TestSendKeyCursorKeysFollowDECCKM/end
        key_test.go:60: DECCKM set: got "\x1b[F", want "\x1bOF"
    
  • with the fix: go test -race ./... green in vt/, repeated 5×; gofmt -l empty; go build ./... clean.

Bounds, honestly: this was run on darwin/arm64 with Go 1.26 only. I did not exercise it against a real ncurses application — the terminfo entry above is the evidence for that claim, not a live run.

Two things a reviewer will notice

The test starts goroutines. SendKey writes into the emulator's io.Pipe, which blocks until something reads, so the read has to be waiting before the send. That is the same blocking #953 is addressing; if that lands first, the helper simplifies and I am happy to follow up.

Modified cursor keys are a separate defect and not in this PR. KeyPressEvent{Code: KeyUp, Mod: ModCtrl} matches no case and falls to default:, where if key.Mod == 0 means nothing is written at all — the keystroke disappears. xterm sends ESC [ 1 ; 5 A. I have kept it out to keep this PR single-purpose, and I want to flag that this is a different axis from the // TODO: Support Kitty, CSI u, and XTerm modifyOtherKeys at the top of the function: CSI 1 ; m A is plain xterm cursor-key modifier encoding, not modifyOtherKeys and not the Kitty protocol. Happy to open an issue or a follow-up PR, whichever you prefer.

SendKey already computes `ack` from ModeCursorKeys and uses it to choose
between SS3 and CSI for the four arrow keys, but Home and End twenty
lines below it emit CSI unconditionally. Under DECCKM they should be
`ESC O H` and `ESC O F`.

The forms are the ones this stack already recognizes on the way in:
ultraviolet's key table lists them under its own "Application Cursor Key
Mode (DECCKM)" heading, next to `ESC O A`..`ESC O D`. So an Emulator
whose output is decoded by ultraviolet round-trips the arrows and loses
Home and End. terminfo agrees — xterm-256color pairs `smkx=\E[?1h` with
`khome=\EOH` and `kend=\EOF` — which is why an ncurses application that
called keypad(true) does not recognize the CSI forms as Home or End.

The added test covers all six cursor keys in both modes, so the arrows
document the behaviour Home and End now match.
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.

1 participant