Handle CSI intermediate bytes instead of printing the final byte - #3
Open
kay-ws wants to merge 1 commit into
Open
Handle CSI intermediate bytes instead of printing the final byte#3kay-ws wants to merge 1 commit into
kay-ws wants to merge 1 commit into
Conversation
ECMA-48 5.4 defines a control sequence as "CSI P...P I...I F". The Intermediate Bytes I (02/00 to 02/15) "together with the Final Byte F identify the control function", so they can neither be parsed as parameters nor skipped. The state machine had no state for them. On encountering an intermediate byte in EScsi it fell through to the default entry, which resets the state to ESnormal. The final byte then arrived outside of any sequence and was printed as a literal character. For example "CSI 2 SP q" (DECSCUSR) left a stray 'q' on screen at the cursor position. Applications that select a cursor shape per editing mode emit it on every cursor movement, so the character accumulated across the line. ECMA-48 table 4 lists further functions using a single intermediate byte 02/00 (SL, SR, GSM, ...), which were affected in the same way. Add EScsiInter so that intermediate bytes are tracked and the final byte is consumed as part of the sequence. This also keeps "CSI 2 SP q" (DECSCUSR) distinct from "CSI 2 q" (DECLL), which is implemented as set_led. The entry for '%' carrying the comment "otherwise the trailing 'm' gets printed" addressed the same problem for a single byte. It is moved into the new state, where the trailing 'm' reaches the SGR handler through EScsiInter. ECMA-48 assigns no function to an intermediate byte followed by 06/13, so this does not shadow a standard sequence. Section numbers in the escape_sequences[] comments were off by one from ESgreater onwards; corrected while adding the new section. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Control sequences containing an intermediate byte have their final byte printed to the screen as a literal character.
Expected
AB, actualAqB. The stray character lands at the cursor position, so an application emitting such a sequence on every cursor movement accumulates it across the line.Cause
ECMA-48 5.4 defines a control sequence as
CSI P...P I...I F, where the Intermediate BytesI(02/00 to 02/15) "together with the Final Byte F identify the control function".The state machine has no state for intermediate bytes. On encountering one in
EScsi, the lookup inescape_map[]misses and falls through to the default entry ofescape_sequences[], which resets the state toESnormal. The final byte then arrives outside of any sequence and is processed as ordinary text.This affects every sequence carrying an intermediate byte. ECMA-48 table 4 lists the functions that use a single intermediate byte 02/00 (SL, SR, GSM, ...). A frequently emitted one is DECSCUSR (
CSI Ps SP q), used by applications that select a cursor shape per editing mode.Changes
Add
EScsiInter, entered fromEScsion an intermediate byte. It consumes any further intermediate bytes and then the final byte, so nothing reaches the screen. Sequences arriving here are not implemented by fbterm and are discarded silently.This also keeps
CSI 2 SP q(DECSCUSR) distinct fromCSI 2 q(DECLL), which is implemented asset_led— the two are different control functions and must not be conflated.The existing entry for
'%'inEScsi, carrying the comment "otherwise the trailing 'm' gets printed", addressed the same problem for a single byte. It is moved into the new state, where the trailingmreaches the SGR handler throughEScsiInter. ECMA-48 assigns no function to an intermediate byte followed by 06/13, so this does not shadow a standard sequence.ESkeepis used asNR_STATESand terminates the section walk ininit_state(), so the new state is inserted before it; a comment now records that constraint.Section numbers in the
escape_sequences[]comments were off by one fromESgreateronwards; corrected while adding the new section.Testing
A CSI 2 SP q BAqBABCSI 0 % mC CSI 2 q D(DECLL)CDCDThe second line checks that moving the
'%'entry did not regress it; the third checks that DECSCUSR and DECLL remain distinct.Built and run on x86_64 (Arch, Linux 6.16) and armv7l (Debian bookworm, Linux 3.10), reading
/dev/fb0before and after on both. Colour output, UTF-8 input via uim-fep and scrollback are unaffected.