Skip to content

Commit ee3f12b

Browse files
committed
Revert d651e3a to restore BCE behavior. Issue 12723
This reverts commit d651e3a ("Use defaults rather than current SGR color when scrolling lines into view"). The reverted commit broke BCE (Background Color Erase) compliance, which vim relies on. When vim sets a background color and scrolls, it expects scrolled-in lines to use the current SGR background color. After the reverted commit, scrolled-in lines used terminal defaults instead, causing inconsistent background colors in vim. BCE behavior: when new lines scroll into view, they are filled with the current SGR background color (via grid's defaultChar). This is synchronized from the terminal before each token executes. Added unit tests for BCE behavior: - testBCE_ScrollingUsesDefaultCharBackgroundColor - testBCE_ScrollRectUsesDefaultCharBackgroundColor Note: This may reintroduce the behavior reported in issue 12512, where scrolled-in lines retain their background color after SGR 49 reset. However, this is correct BCE behavior - SGR 49 affects future operations, not cells already written during scroll.
1 parent 5243969 commit ee3f12b

File tree

6 files changed

+170
-62
lines changed

6 files changed

+170
-62
lines changed

ModernTests/VT100GridTests.swift

Lines changed: 117 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -952,7 +952,7 @@ class VT100GridTests: XCTestCase {
952952
grid.markAllCharsDirty(false, updateTimestamps: false)
953953

954954
let rect = VT100GridRect(x: 1, y: 1, width: 2, height: 2)
955-
grid.scroll(rect, downBy: 0, softBreak: false, fill: .zero)
955+
grid.scroll(rect, downBy: 0, softBreak: false)
956956

957957
// Content should be unchanged
958958
let expectedGrid = [
@@ -981,7 +981,7 @@ class VT100GridTests: XCTestCase {
981981
grid.markAllCharsDirty(false, updateTimestamps: false)
982982

983983
let rect = VT100GridRect(x: 1, y: 1, width: 2, height: 2)
984-
grid.scroll(rect, downBy: 1, softBreak: false, fill: .zero)
984+
grid.scroll(rect, downBy: 1, softBreak: false)
985985

986986
let expectedContent = [
987987
"abcd\n",
@@ -1013,7 +1013,7 @@ class VT100GridTests: XCTestCase {
10131013
grid.markAllCharsDirty(false, updateTimestamps: false)
10141014

10151015
let rect = VT100GridRect(x: 1, y: 1, width: 2, height: 2)
1016-
grid.scroll(rect, downBy: -1, softBreak: false, fill: .zero)
1016+
grid.scroll(rect, downBy: -1, softBreak: false)
10171017

10181018
let expected = [
10191019
"abcd\n",
@@ -1045,7 +1045,7 @@ class VT100GridTests: XCTestCase {
10451045
grid.markAllCharsDirty(false, updateTimestamps: false)
10461046

10471047
let rect = VT100GridRect(x: 1, y: 1, width: 3, height: 3)
1048-
grid.scroll(rect, downBy: 2, softBreak: false, fill: .zero)
1048+
grid.scroll(rect, downBy: 2, softBreak: false)
10491049

10501050
let expectedContent = [
10511051
"abcde\n",
@@ -1075,7 +1075,7 @@ class VT100GridTests: XCTestCase {
10751075
grid.markAllCharsDirty(false, updateTimestamps: false)
10761076

10771077
let rect = VT100GridRect(x: 1, y: 1, width: 3, height: 3)
1078-
grid.scroll(rect, downBy: -2, softBreak: false, fill: .zero)
1078+
grid.scroll(rect, downBy: -2, softBreak: false)
10791079

10801080
let expected = [
10811081
"abcde\n",
@@ -1103,7 +1103,7 @@ class VT100GridTests: XCTestCase {
11031103
grid.markAllCharsDirty(false, updateTimestamps: false)
11041104

11051105
let rect = VT100GridRect(x: 1, y: 1, width: 3, height: 3)
1106-
grid.scroll(rect, downBy: 3, softBreak: false, fill: .zero)
1106+
grid.scroll(rect, downBy: 3, softBreak: false)
11071107

11081108
let expected = [
11091109
"abcde\n",
@@ -1131,7 +1131,7 @@ class VT100GridTests: XCTestCase {
11311131
grid.markAllCharsDirty(false, updateTimestamps: false)
11321132

11331133
let rect = VT100GridRect(x: 1, y: 1, width: 3, height: 3)
1134-
grid.scroll(rect, downBy: -3, softBreak: false, fill: .zero)
1134+
grid.scroll(rect, downBy: -3, softBreak: false)
11351135

11361136
let expected = [
11371137
"abcde\n",
@@ -1159,7 +1159,7 @@ class VT100GridTests: XCTestCase {
11591159
grid.markAllCharsDirty(false, updateTimestamps: false)
11601160

11611161
let rect = VT100GridRect(x: 1, y: 1, width: 3, height: 3)
1162-
grid.scroll(rect, downBy: 4, softBreak: false, fill: .zero)
1162+
grid.scroll(rect, downBy: 4, softBreak: false)
11631163

11641164
let expected = [
11651165
"abcde\n",
@@ -1187,7 +1187,7 @@ class VT100GridTests: XCTestCase {
11871187
grid.markAllCharsDirty(false, updateTimestamps: false)
11881188

11891189
let rect = VT100GridRect(x: 1, y: 1, width: 3, height: 3)
1190-
grid.scroll(rect, downBy: -4, softBreak: false, fill: .zero)
1190+
grid.scroll(rect, downBy: -4, softBreak: false)
11911191

11921192
let expectedContent = [
11931193
"abcde\n",
@@ -1213,7 +1213,7 @@ class VT100GridTests: XCTestCase {
12131213
grid.markAllCharsDirty(false, updateTimestamps: false)
12141214

12151215
let rect = VT100GridRect(x: 0, y: 1, width: 3, height: 2)
1216-
grid.scroll(rect, downBy: -1, softBreak: false, fill: .zero)
1216+
grid.scroll(rect, downBy: -1, softBreak: false)
12171217

12181218
// The broken EOL_DWC placeholders at rows 0 and 1 should be cleaned up ('.'),
12191219
// blank row inserted at row 2, and the intact DWC_RIGHT at row 3 should remain.
@@ -1250,7 +1250,7 @@ class VT100GridTests: XCTestCase {
12501250

12511251
// Scroll rectangle x=0,y=1,width=3,height=2 down by 1
12521252
let rect = VT100GridRect(x: 0, y: 1, width: 3, height: 2)
1253-
grid.scroll(rect, downBy: 1, softBreak: false, fill: .zero)
1253+
grid.scroll(rect, downBy: 1, softBreak: false)
12541254

12551255
// After scroll:
12561256
// Row 0: the broken split‐DWC at top is cleaned up → "ab."
@@ -1289,7 +1289,7 @@ class VT100GridTests: XCTestCase {
12891289

12901290
// Scroll the entire 3×3 region up by 1 (downBy: -1)
12911291
let rect = VT100GridRect(x: 0, y: 0, width: 3, height: 3)
1292-
grid.scroll(rect, downBy: -1, softBreak: false, fill: .zero)
1292+
grid.scroll(rect, downBy: -1, softBreak: false)
12931293

12941294
// After scroll:
12951295
// Row 0 <- old Row 1 ("de>\n")
@@ -1325,7 +1325,7 @@ class VT100GridTests: XCTestCase {
13251325
grid.markAllCharsDirty(false, updateTimestamps: false)
13261326

13271327
let rect = VT100GridRect(x: 1, y: 0, width: 3, height: 6)
1328-
grid.scroll(rect, downBy: -1, softBreak: false, fill: .zero)
1328+
grid.scroll(rect, downBy: -1, softBreak: false)
13291329

13301330
let expectedContent = [
13311331
"a\0g\0e\n",
@@ -1356,7 +1356,7 @@ class VT100GridTests: XCTestCase {
13561356
grid.markAllCharsDirty(false, updateTimestamps: false)
13571357

13581358
let rect = VT100GridRect(x: 0, y: 1, width: 5, height: 3)
1359-
grid.scroll(rect, downBy: -1, softBreak: false, fill: .zero)
1359+
grid.scroll(rect, downBy: -1, softBreak: false)
13601360

13611361
let expectedContent = [
13621362
"abcd\n",
@@ -1392,7 +1392,7 @@ class VT100GridTests: XCTestCase {
13921392
grid.markAllCharsDirty(false, updateTimestamps: false)
13931393

13941394
let rect = VT100GridRect(x: 1, y: 0, width: 4, height: 5)
1395-
grid.scroll(rect, downBy: -1, softBreak: false, fill: .zero)
1395+
grid.scroll(rect, downBy: -1, softBreak: false)
13961396

13971397
let expectedContent = [
13981398
"a\0fgh\n",
@@ -1423,7 +1423,7 @@ class VT100GridTests: XCTestCase {
14231423
grid.markAllCharsDirty(false, updateTimestamps: false)
14241424

14251425
let rect = VT100GridRect(x: 0, y: 0, width: 4, height: 5)
1426-
grid.scroll(rect, downBy: -1, softBreak: false, fill: .zero)
1426+
grid.scroll(rect, downBy: -1, softBreak: false)
14271427

14281428
let expectedContent = [
14291429
"e-fg\n",
@@ -1454,7 +1454,7 @@ class VT100GridTests: XCTestCase {
14541454

14551455
// Empty rect → no-op
14561456
let rect = VT100GridRect(x: 1, y: 1, width: 0, height: 0)
1457-
grid.scroll(rect, downBy: 1, softBreak: false, fill: .zero)
1457+
grid.scroll(rect, downBy: 1, softBreak: false)
14581458

14591459
// Content unchanged
14601460
let expectedContent = [
@@ -1485,7 +1485,7 @@ class VT100GridTests: XCTestCase {
14851485
grid.markAllCharsDirty(false, updateTimestamps: false)
14861486

14871487
let rect = VT100GridRect(x: 0, y: 1, width: 3, height: 2)
1488-
grid.scroll(rect, downBy: -1, softBreak: false, fill: .zero)
1488+
grid.scroll(rect, downBy: -1, softBreak: false)
14891489

14901490
let expectedContent = [
14911491
"ab\n",
@@ -1514,7 +1514,7 @@ class VT100GridTests: XCTestCase {
15141514
grid.markAllCharsDirty(false, updateTimestamps: false)
15151515

15161516
let rect = VT100GridRect(x: 0, y: 1, width: 3, height: 3)
1517-
grid.scroll(rect, downBy: 1, softBreak: false, fill: .zero)
1517+
grid.scroll(rect, downBy: 1, softBreak: false)
15181518

15191519
let expectedContent = [
15201520
"ab\n",
@@ -1543,7 +1543,7 @@ class VT100GridTests: XCTestCase {
15431543
grid.markAllCharsDirty(false, updateTimestamps: false)
15441544

15451545
let rect = VT100GridRect(x: 0, y: 1, width: 3, height: 3)
1546-
grid.scroll(rect, downBy: -1, softBreak: false, fill: .zero)
1546+
grid.scroll(rect, downBy: -1, softBreak: false)
15471547

15481548
let expectedContent = [
15491549
"ab\n",
@@ -1572,7 +1572,7 @@ class VT100GridTests: XCTestCase {
15721572
grid.markAllCharsDirty(false, updateTimestamps: false)
15731573

15741574
let rect = VT100GridRect(x: 0, y: 1, width: 4, height: 3)
1575-
grid.scroll(rect, downBy: 1, softBreak: false, fill: .zero)
1575+
grid.scroll(rect, downBy: 1, softBreak: false)
15761576

15771577
let expectedContent = [
15781578
"abcd\n",
@@ -1602,7 +1602,7 @@ class VT100GridTests: XCTestCase {
16021602
grid.markAllCharsDirty(false, updateTimestamps: false)
16031603

16041604
let rect = VT100GridRect(x: 0, y: 1, width: 4, height: 3)
1605-
grid.scroll(rect, downBy: 2, softBreak: false, fill: .zero)
1605+
grid.scroll(rect, downBy: 2, softBreak: false)
16061606

16071607
let expectedContent = [
16081608
"abcd\n",
@@ -1630,7 +1630,7 @@ class VT100GridTests: XCTestCase {
16301630
grid.markAllCharsDirty(false, updateTimestamps: false)
16311631

16321632
let rect = VT100GridRect(x: 0, y: 1, width: 4, height: 3)
1633-
grid.scroll(rect, downBy: -1, softBreak: false, fill: .zero)
1633+
grid.scroll(rect, downBy: -1, softBreak: false)
16341634

16351635
let expectedContent = [
16361636
"abcd\n",
@@ -1658,7 +1658,7 @@ class VT100GridTests: XCTestCase {
16581658
grid.markAllCharsDirty(false, updateTimestamps: false)
16591659

16601660
let rect = VT100GridRect(x: 0, y: 1, width: 4, height: 3)
1661-
grid.scroll(rect, downBy: -2, softBreak: false, fill: .zero)
1661+
grid.scroll(rect, downBy: -2, softBreak: false)
16621662

16631663
let expectedContent = [
16641664
"abcd\n",
@@ -1686,7 +1686,7 @@ class VT100GridTests: XCTestCase {
16861686
grid.markAllCharsDirty(false, updateTimestamps: false)
16871687

16881688
let rect = VT100GridRect(x: 1, y: 1, width: 3, height: 3)
1689-
grid.scroll(rect, downBy: 1, softBreak: false, fill: .zero)
1689+
grid.scroll(rect, downBy: 1, softBreak: false)
16901690

16911691
let expectedContent = [
16921692
"abcd",
@@ -1714,7 +1714,7 @@ class VT100GridTests: XCTestCase {
17141714
grid.markAllCharsDirty(false, updateTimestamps: false)
17151715

17161716
let rect = VT100GridRect(x: 1, y: 1, width: 3, height: 3)
1717-
grid.scroll(rect, downBy: 2, softBreak: false, fill: .zero)
1717+
grid.scroll(rect, downBy: 2, softBreak: false)
17181718

17191719
let expectedContent = [
17201720
"abcd",
@@ -1745,7 +1745,7 @@ class VT100GridTests: XCTestCase {
17451745
grid.markAllCharsDirty(false, updateTimestamps: false)
17461746

17471747
let rect = VT100GridRect(x: 1, y: 1, width: 3, height: 3)
1748-
grid.scroll(rect, downBy: 1, softBreak: false, fill: .zero)
1748+
grid.scroll(rect, downBy: 1, softBreak: false)
17491749

17501750
let expectedContent = [
17511751
"abcd",
@@ -1773,7 +1773,7 @@ class VT100GridTests: XCTestCase {
17731773
grid.markAllCharsDirty(false, updateTimestamps: false)
17741774

17751775
let rect = VT100GridRect(x: 1, y: 1, width: 3, height: 3)
1776-
grid.scroll(rect, downBy: -1, softBreak: false, fill: .zero)
1776+
grid.scroll(rect, downBy: -1, softBreak: false)
17771777

17781778
let expectedContent = [
17791779
"abcd",
@@ -1803,7 +1803,7 @@ class VT100GridTests: XCTestCase {
18031803
grid.markAllCharsDirty(false, updateTimestamps: false)
18041804

18051805
let rect = VT100GridRect(x: 1, y: 1, width: 3, height: 3)
1806-
grid.scroll(rect, downBy: -1, softBreak: false, fill: .zero)
1806+
grid.scroll(rect, downBy: -1, softBreak: false)
18071807

18081808
let expectedContent = [
18091809
"abc\n",
@@ -1831,7 +1831,7 @@ class VT100GridTests: XCTestCase {
18311831
grid.markAllCharsDirty(false, updateTimestamps: false)
18321832

18331833
let rect = VT100GridRect(x: 1, y: 1, width: 3, height: 3)
1834-
grid.scroll(rect, downBy: -2, softBreak: false, fill: .zero)
1834+
grid.scroll(rect, downBy: -2, softBreak: false)
18351835

18361836
let expectedContent = [
18371837
"abcd",
@@ -3998,6 +3998,93 @@ class VT100GridTests: XCTestCase {
39983998
lineBuffer.removeLastRawLine()
39993999
XCTAssertEqual(lineBuffer.lineStrings, [])
40004000
}
4001+
4002+
// MARK: - BCE (Background Color Erase) Tests
4003+
4004+
/// Test for issue 12723: When scrolling, newly cleared lines should use
4005+
/// the grid's defaultChar background color (BCE behavior).
4006+
func testBCE_ScrollingUsesDefaultCharBackgroundColor() {
4007+
let width: Int32 = 10
4008+
let height: Int32 = 4
4009+
let grid = VT100Grid(size: VT100GridSize(width: width, height: height), delegate: nil)!
4010+
let lineBuffer = LineBuffer(blockSize: 1000)
4011+
4012+
// Fill the grid with text
4013+
let initialLines = [
4014+
"line1\n",
4015+
"line2\n",
4016+
"line3\n",
4017+
"line4"
4018+
]
4019+
append(strings: initialLines, toGrid: grid, lineBuffer: lineBuffer)
4020+
4021+
// Set up a custom defaultChar with orange 24-bit background color
4022+
var orangeDefaultChar = screen_char_t()
4023+
orangeDefaultChar.code = 0
4024+
orangeDefaultChar.backgroundColorMode = UInt32(ColorMode24bit.rawValue)
4025+
orangeDefaultChar.backgroundColor = 255 // red
4026+
orangeDefaultChar.bgGreen = 165
4027+
orangeDefaultChar.bgBlue = 0
4028+
grid.defaultChar = orangeDefaultChar
4029+
4030+
// Scroll the grid down (which clears the bottom line)
4031+
grid.cursorX = 0
4032+
grid.cursorY = 3 // bottom line
4033+
grid.moveCursorDownOneLineScrolling(into: lineBuffer,
4034+
unlimitedScrollback: true,
4035+
useScrollbackWithRegion: false,
4036+
willScroll: nil,
4037+
sentToLineBuffer: nil)
4038+
4039+
// The newly scrolled-in line (line 3, 0-indexed) should have the orange background
4040+
let line = grid.immutableScreenChars(atLineNumber: 3)!
4041+
4042+
// Check that the empty cells have 24-bit orange background
4043+
XCTAssertEqual(line[0].backgroundColorMode, UInt32(ColorMode24bit.rawValue),
4044+
"BCE: scrolled-in line should have 24-bit background color mode from defaultChar")
4045+
XCTAssertEqual(line[0].backgroundColor, 255,
4046+
"BCE: scrolled-in line should have red=255 from defaultChar")
4047+
XCTAssertEqual(line[0].bgGreen, 165,
4048+
"BCE: scrolled-in line should have green=165 from defaultChar")
4049+
XCTAssertEqual(line[0].bgBlue, 0,
4050+
"BCE: scrolled-in line should have blue=0 from defaultChar")
4051+
}
4052+
4053+
/// Test for issue 12723: scrollRect:downBy: should use defaultChar for cleared regions.
4054+
func testBCE_ScrollRectUsesDefaultCharBackgroundColor() {
4055+
let grid = gridFromCompactLinesWithContinuationMarks(
4056+
"abcdef!\n" +
4057+
"ghijkl!\n" +
4058+
"mnopqr!\n" +
4059+
"stuvwx!"
4060+
)
4061+
4062+
// Set up a custom defaultChar with orange 24-bit background color
4063+
var orangeDefaultChar = screen_char_t()
4064+
orangeDefaultChar.code = 0
4065+
orangeDefaultChar.backgroundColorMode = UInt32(ColorMode24bit.rawValue)
4066+
orangeDefaultChar.backgroundColor = 255 // red
4067+
orangeDefaultChar.bgGreen = 165
4068+
orangeDefaultChar.bgBlue = 0
4069+
grid.defaultChar = orangeDefaultChar
4070+
4071+
// Scroll the entire rect up by 1 (this clears the bottom line)
4072+
let rect = VT100GridRect(origin: VT100GridCoord(x: 0, y: 0),
4073+
size: VT100GridSize(width: grid.size.width, height: grid.size.height))
4074+
grid.scroll(rect, downBy: -1, softBreak: false)
4075+
4076+
// The newly cleared bottom line should have the orange background
4077+
let line = grid.immutableScreenChars(atLineNumber: 3)!
4078+
4079+
XCTAssertEqual(line[0].backgroundColorMode, UInt32(ColorMode24bit.rawValue),
4080+
"BCE: scrollRect cleared line should have 24-bit background color mode")
4081+
XCTAssertEqual(line[0].backgroundColor, 255,
4082+
"BCE: scrollRect cleared line should have red=255")
4083+
XCTAssertEqual(line[0].bgGreen, 165,
4084+
"BCE: scrollRect cleared line should have green=165")
4085+
XCTAssertEqual(line[0].bgBlue, 0,
4086+
"BCE: scrollRect cleared line should have blue=0")
4087+
}
40014088
}
40024089

40034090
extension VT100Grid {

ModernTests/VT100ScreenTests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1480,6 +1480,7 @@ class VT100ScreenTests: XCTestCase {
14801480
".....!\n" +
14811481
".....!")
14821482
}
1483+
14831484
}
14841485

14851486
// a very simple LCG-based RNG

sources/VT100Grid.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -321,10 +321,7 @@ makeCursorLineSoft:(BOOL)makeCursorLineSoft;
321321
// Clears the left-over region.
322322
// If |softBreak| is YES then the soft line break on the top line (when scrolling down) or bottom
323323
// line (when scrolling up) is preserved. Otherwise it is made hard.
324-
- (void)scrollRect:(VT100GridRect)rect
325-
downBy:(int)direction
326-
softBreak:(BOOL)softBreak
327-
fillChar:(screen_char_t)fillChar;
324+
- (void)scrollRect:(VT100GridRect)rect downBy:(int)direction softBreak:(BOOL)softBreak;
328325

329326
// Load contents from a DVR frame.
330327
- (void)setContentsFromDVRFrame:(const screen_char_t*)s

0 commit comments

Comments
 (0)