[PR #14936] Ensure that delayed EOL wrap is reset when necessary #30316

Open
opened 2026-01-31 09:40:02 +00:00 by claunia · 0 comments
Owner

Original Pull Request: https://github.com/microsoft/terminal/pull/14936

State: closed
Merged: Yes


When a character is written in the last column of a row, the cursor
doesn't move, but instead sets a "delayed EOL wrap" flag. If another
character is then output while that flag is still set, the cursor moves
to the start of the next line, before writing to the buffer.

That flag is supposed to be reset when certain control sequences are
executed, but prior to now we haven't always handled that correctly.
With this PR, we should be resetting the flag appropriately in all the
places that it's expected to be reset.

For the most part, I'm following the DEC STD 070 reference, which lists
a bunch of operations that are intended to reset the delayed wrap flag:

DECSTBM, DECSWL, DECDWL, DECDHL, setting DECCOLM and DECOM,
resetting DECCOLM, DECOM, and DECAWM, CUU, CUD, CUF, CUB,
CUP, HVP, BS, LF, VT, FF, CR, IND, RI, NEL, ECH,
DCH, ICH, EL, DECSEL, DL, IL, ED, and DECSED.

We were already resetting the flag for any of the operations that
performed cursor movement, since that always triggers a reset for us.
However, I've now also added manual resets in those ops that weren't
moving the cursor.

Horizontal tabs are a special case, though. Technically the standard
says they should reset the flag, but most DEC terminals never followed
that rule, and most modern terminals agree that it's best for a tab to
leave the flag as it is. Our implementation now does that too.

But as mentioned above, we automatically reset the flag on any cursor
movement, so the tab operation had to be handled as a special case,
saving and restoring the flag when the cursor is updated.

Another flaw in our implementation was that we should have been saving
and restoring the flag as part of the cursor state in the DECSC and
DECRC operations. That's now been fixed in this PR too.

I should also mention there was a change I had to make to the conpty
renderer, because it was sometimes using an EL sequence while the
terminal was in the delayed EOL wrap state. This would reset the flag,
and break subsequent output, so I've now added a check to prevent that
from happening.

Validation Steps Performed

I've added some unit tests that confirm the operations listed above are
now resetting the delayed EOL wrap as expected, and I've expanded the
existing CursorSaveRestore test to make sure the flag is being saved
and restored correctly.

I've also manually confirmed that the test case in issue #3177 now
matches XTerm's output, and I've confirmed that the results of the
wraptest script1 now match XTerm's results.

Closes #3177

**Original Pull Request:** https://github.com/microsoft/terminal/pull/14936 **State:** closed **Merged:** Yes --- When a character is written in the last column of a row, the cursor doesn't move, but instead sets a "delayed EOL wrap" flag. If another character is then output while that flag is still set, the cursor moves to the start of the next line, before writing to the buffer. That flag is supposed to be reset when certain control sequences are executed, but prior to now we haven't always handled that correctly. With this PR, we should be resetting the flag appropriately in all the places that it's expected to be reset. For the most part, I'm following the DEC STD 070 reference, which lists a bunch of operations that are intended to reset the delayed wrap flag: `DECSTBM`, `DECSWL`, `DECDWL`, `DECDHL`, setting `DECCOLM` and `DECOM`, resetting `DECCOLM`, `DECOM`, and `DECAWM`, `CUU`, `CUD`, `CUF`, `CUB`, `CUP`, `HVP`, `BS`, `LF`, `VT`, `FF`, `CR`, `IND`, `RI`, `NEL`, `ECH`, `DCH`, `ICH`, `EL`, `DECSEL`, `DL`, `IL`, `ED`, and `DECSED`. We were already resetting the flag for any of the operations that performed cursor movement, since that always triggers a reset for us. However, I've now also added manual resets in those ops that weren't moving the cursor. Horizontal tabs are a special case, though. Technically the standard says they should reset the flag, but most DEC terminals never followed that rule, and most modern terminals agree that it's best for a tab to leave the flag as it is. Our implementation now does that too. But as mentioned above, we automatically reset the flag on any cursor movement, so the tab operation had to be handled as a special case, saving and restoring the flag when the cursor is updated. Another flaw in our implementation was that we should have been saving and restoring the flag as part of the cursor state in the `DECSC` and `DECRC` operations. That's now been fixed in this PR too. I should also mention there was a change I had to make to the conpty renderer, because it was sometimes using an `EL` sequence while the terminal was in the delayed EOL wrap state. This would reset the flag, and break subsequent output, so I've now added a check to prevent that from happening. ## Validation Steps Performed I've added some unit tests that confirm the operations listed above are now resetting the delayed EOL wrap as expected, and I've expanded the existing `CursorSaveRestore` test to make sure the flag is being saved and restored correctly. I've also manually confirmed that the test case in issue #3177 now matches XTerm's output, and I've confirmed that the results of the wraptest script[^1] now match XTerm's results. [^1]: https://github.com/mattiase/wraptest/ Closes #3177
claunia added the pull-request label 2026-01-31 09:40:02 +00:00
Sign in to join this conversation.
No Label pull-request
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#30316