Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Sources/SwiftTerm/Apple/AppleTerminalView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1284,6 +1284,16 @@ extension TerminalView {
}
var placeholderImageCache: [UInt32: TTImage] = [:]

#if os(macOS)
// Clear the invalidated region before painting. We fill only cells that carry
// an explicit background; default-background cells rely on transparent backing-
// store pixels showing the layer's background color. AppKit clears the backing
// store only on a full-view redraw, so a partial repaint (a restricted DECSTBM
// scroll region, line insert/delete) otherwise keeps stale glyphs/backgrounds.
// Clear to transparent — not fill — so a translucent background is preserved.
context.clear(dirtyRect)
#endif

for row in firstRow...lastRow {
if row < 0 {
continue
Expand Down Expand Up @@ -1773,6 +1783,14 @@ extension TerminalView {
let oh = region.height
let oy = region.origin.y
region = CGRect (x: 0, y: 0, width: frame.width, height: oh + oy)
} else {
// Region ends mid-screen (a restricted DECSTBM region): extend the
// invalidation down by one cell so the sub-cell remainder just below the
// band's bottom row (descenders / tall unicode) is cleared too. Previously
// only rowEnd == rows-1 got this, leaving a one-row ghost below the region.
let extra = cellDimension.height
let newY = max (0, region.origin.y - extra)
region = CGRect (x: 0, y: newY, width: frame.width, height: region.maxY - newY)
}
#if canImport(MetalKit)
if metalView != nil {
Expand Down
30 changes: 24 additions & 6 deletions Sources/SwiftTerm/Terminal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2523,6 +2523,11 @@ open class Terminal {
}
// this.maxRange();
updateRange (startLine: buffer.y, endLine: buffer.scrollBottom)
// A restricted region leaves stale pixels / a bottom-edge ghost outside
// [y, scrollBottom] on the CG renderer (as in scroll()); the range above
// already covers full-screen, so widen to the whole viewport only when the
// region is restricted.
refreshScrolledRegion(top: buffer.scrollTop, bottom: buffer.scrollBottom, canBlit: true)
}

//
Expand Down Expand Up @@ -4750,7 +4755,7 @@ open class Terminal {
}
}
// this.maxRange();
updateRange (startLine: buffer.scrollTop, endLine: buffer.scrollBottom)
refreshScrolledRegion(top: buffer.scrollTop, bottom: buffer.scrollBottom, canBlit: false)
}

//
Expand Down Expand Up @@ -4786,7 +4791,7 @@ open class Terminal {
}
}
// this.maxRange();
updateRange (startLine: buffer.scrollTop, endLine: buffer.scrollBottom)
refreshScrolledRegion(top: buffer.scrollTop, bottom: buffer.scrollBottom, canBlit: false)
}

//
Expand Down Expand Up @@ -4863,6 +4868,11 @@ open class Terminal {

// this.maxRange();
updateRange (startLine: buffer.y, endLine: buffer.scrollBottom)
// A restricted region leaves stale pixels / a bottom-edge ghost outside
// [y, scrollBottom] on the CG renderer (as in scroll()); the range above
// already covers full-screen, so widen to the whole viewport only when the
// region is restricted.
refreshScrolledRegion(top: buffer.scrollTop, bottom: buffer.scrollBottom, canBlit: true)
}

//
Expand Down Expand Up @@ -5260,6 +5270,16 @@ open class Terminal {

var blankLine: BufferLine = BufferLine(cols: 0)

/// Flag the scrolled region dirty. The CoreGraphics renderer now clears any
/// dirtied region before painting (see AppleTerminalView), so flagging just
/// [top, bottom] fixes the stale rows / bottom ghost — no whole-viewport repaint
/// needed. Full-screen + scrollback (`canBlit`) keeps the cheap path.
private func refreshScrolledRegion(top: Int, bottom: Int, canBlit: Bool) {
if top != 0 || bottom != rows - 1 || !canBlit {
updateRange(startLine: top, endLine: bottom)
}
}

public func scroll (isWrapped: Bool = false)
{
let buffer = self.buffer
Expand Down Expand Up @@ -5385,9 +5405,7 @@ open class Terminal {
updateRange (scrollTop, scrolling: true)
updateRange (scrollBottom, scrolling: true)

if !hasScrollback {
updateRange(startLine: scrollTop, endLine: scrollBottom)
}
refreshScrolledRegion(top: scrollTop, bottom: scrollBottom, canBlit: hasScrollback)

if buffer.hasAnyImages {
updateKittyRelativePlacementsForCurrentBuffer()
Expand Down Expand Up @@ -5826,7 +5844,7 @@ open class Terminal {
}
buffer.lines [topRow] = buffer.getBlankLine (attribute: eraseAttr ())
}
updateRange (startLine: buffer.scrollTop, endLine: buffer.scrollBottom)
refreshScrolledRegion(top: buffer.scrollTop, bottom: buffer.scrollBottom, canBlit: false)
}
} else if buffer.y > 0 {
buffer.y -= 1
Expand Down