From eefa5d1ffcae094088c37040ce7eaa144792e2ae Mon Sep 17 00:00:00 2001 From: Miguel de Icaza Date: Mon, 17 Apr 2023 23:04:22 -0400 Subject: [PATCH] Better approach at rendering the bottom exposed region of the buffer, and in the process, I cleaned up some historical baggage and a nice upgrade. The nice upgrade is that I am using the exposed region as intended for the Mac, over time, the code regressed and was redrawing the whole buffer, now it does what it is supposed to do. The parameter "offset" to "drawTerminalContents" was deprecated when I moved to the UIScrollView backing for the iOS terminal, so I have eliminated it. Rather than computing "remains" at the start, we compute at the end, and rather than the float->int->float conversions, we use: bounds.height.truncatingRemainder(dividingBy: cellHeight) Additionally, given that there is a font change redraw issue not addressed here, I am keeping the old clearing code (and I have fixed it now), but I have also implemented the alterantive which should draw a lot less, so as soon as I fix the font change issue, I can remove the code from the caller of "drawTerminalContents". --- .gitignore | 6 +++ .../SwiftTerm/Apple/AppleTerminalView.swift | 44 ++++++++++++------- Sources/SwiftTerm/Mac/MacTerminalView.swift | 2 +- Sources/SwiftTerm/iOS/iOSTerminalView.swift | 6 ++- 4 files changed, 40 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 48cb1dbf..cbf4061b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,9 @@ slow-unit-* timeout-* slow-unit-* fuzz-*.log +xcuserdata +crash-* +DerivedData +.deriveddata +.build +.DS_Store diff --git a/Sources/SwiftTerm/Apple/AppleTerminalView.swift b/Sources/SwiftTerm/Apple/AppleTerminalView.swift index ff172dcc..046a089f 100644 --- a/Sources/SwiftTerm/Apple/AppleTerminalView.swift +++ b/Sources/SwiftTerm/Apple/AppleTerminalView.swift @@ -529,30 +529,28 @@ extension TerminalView { // TODO: this should not render any lines outside the dirtyRect - func drawTerminalContents (dirtyRect: TTRect, context: CGContext, offset: CGFloat, bufferOffset: Int) + func drawTerminalContents (dirtyRect: TTRect, context: CGContext, bufferOffset: Int) { let lineDescent = CTFontGetDescent(fontSet.normal) let lineLeading = CTFontGetLeading(fontSet.normal) let yOffset = ceil(lineDescent+lineLeading) - + func calcLineOffset (forRow: Int) -> CGFloat { - cellDimension.height * CGFloat (forRow-bufferOffset+1) + offset + cellDimension.height * CGFloat (forRow-bufferOffset+1) } // draw lines - #if os(iOS) // On iOS, we are drawing the exposed region let cellHeight = cellDimension.height let firstRow = Int (dirtyRect.minY/cellHeight) let lastRow = Int(dirtyRect.maxY/cellHeight) - let remains = CGFloat (lastRow) * cellHeight #else // On Mac, we are drawing the terminal buffer let cellHeight = cellDimension.height - let remains = trunc (dirtyRect.height/cellHeight) * cellHeight - let firstRow = terminal.buffer.yDisp - let lastRow = terminal.rows + terminal.buffer.yDisp + let boundsMaxY = bounds.maxY + let firstRow = terminal.buffer.yDisp+Int ((boundsMaxY-dirtyRect.maxY)/cellHeight) + let lastRow = terminal.buffer.yDisp+Int((boundsMaxY-dirtyRect.minY)/cellHeight) #endif for row in firstRow...lastRow { @@ -618,7 +616,7 @@ extension TerminalView { let line = terminal.buffer.lines [row] let lineInfo = buildAttributedString(row: row, line: line, cols: terminal.cols) let ctline = CTLineCreateWithAttributedString(lineInfo.attrStr) - + var col = 0 for run in CTLineGetGlyphRuns(ctline) as? [CTRun] ?? [] { let runGlyphsCount = CTRunGetGlyphCount(run) @@ -663,13 +661,13 @@ extension TerminalView { origin.y -= missing } #endif - + if col + runGlyphsCount >= terminal.cols { size.width += frame.width - size.width } let rect = CGRect (origin: origin, size: size) - + #if os(macOS) rect.applying(transform).fill(using: .destinationOver) #else @@ -677,7 +675,7 @@ extension TerminalView { #endif context.restoreGState() } - + nativeForegroundColor.set() if runAttributes.keys.contains(.foregroundColor) { @@ -696,6 +694,7 @@ extension TerminalView { col += runGlyphsCount } + // Render any sixel content last if let images = lineInfo.images { let rowBase = frame.height - (CGFloat(row) * cellDimension.height) @@ -712,7 +711,6 @@ extension TerminalView { image.image.draw (in: rect) } } - switch renderMode { case .single: break @@ -724,11 +722,28 @@ extension TerminalView { context.restoreGState() } } - let box = CGRect (x: 0, y: 0, width: bounds.width, height: bounds.height-remains) + +#if os(macOS) + // Fills gaps at the end with the default terminal background + let box = CGRect (x: 0, y: 0, width: bounds.width, height: bounds.height.truncatingRemainder(dividingBy: cellHeight)) if dirtyRect.intersects(box) { nativeBackgroundColor.setFill() context.fill ([box]) } +#elseif false + // Currently the caller on iOS is clearing the entire dirty region due to the ordering of + // font change sizes, but once we fix that, we should remove the clearing of the dirty + // region in the calling code, and enable this code instead. + let lineOffset = calcLineOffset(forRow: lastRow) + let lineOrigin = CGPoint(x: 0, y: frame.height - lineOffset) + + let inter = dirtyRect.intersection(CGRect (x: 0, y: lineOrigin.y, width: bounds.width, height: cellHeight)) + if !inter.isEmpty { + nativeBackgroundColor.setFill() + context.fill ([inter]) + } +#endif + #if os(iOS) if selection.active { let start, end: Position @@ -811,7 +826,6 @@ extension TerminalView { region = CGRect (x: 0, y: 0, width: frame.width, height: oh + oy) } setNeedsDisplay(region) - setNeedsDisplay(bounds) #else // TODO iOS: need to update the code above, but will do that when I get some real // life data being fed into it. diff --git a/Sources/SwiftTerm/Mac/MacTerminalView.swift b/Sources/SwiftTerm/Mac/MacTerminalView.swift index 9a5702a5..c24af982 100644 --- a/Sources/SwiftTerm/Mac/MacTerminalView.swift +++ b/Sources/SwiftTerm/Mac/MacTerminalView.swift @@ -388,7 +388,7 @@ open class TerminalView: NSView, NSTextInputClient, NSUserInterfaceValidations, guard let currentContext = getCurrentGraphicsContext() else { return } - drawTerminalContents (dirtyRect: dirtyRect, context: currentContext, offset: 0, bufferOffset: terminal.buffer.yDisp) + drawTerminalContents (dirtyRect: dirtyRect, context: currentContext, bufferOffset: terminal.buffer.yDisp) } public override func cursorUpdate(with event: NSEvent) diff --git a/Sources/SwiftTerm/iOS/iOSTerminalView.swift b/Sources/SwiftTerm/iOS/iOSTerminalView.swift index 6a0c2e5b..56ac63d6 100644 --- a/Sources/SwiftTerm/iOS/iOSTerminalView.swift +++ b/Sources/SwiftTerm/iOS/iOSTerminalView.swift @@ -963,14 +963,16 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi } // Without these two lines, on font changes, some junk is being displayed + // Once we test the font change, we could disable these two lines, and + // enable the #if false in drawterminalContents that should be coping with this now nativeBackgroundColor.set () - context.clear(dirtyRect) + context.fill ([dirtyRect]) // drawTerminalContents and CoreText expect the AppKit coordinate system context.scaleBy (x: 1, y: -1) context.translateBy(x: 0, y: -frame.height) - drawTerminalContents (dirtyRect: dirtyRect, context: context, offset: 0, bufferOffset: 0) + drawTerminalContents (dirtyRect: dirtyRect, context: context, bufferOffset: 0) } open override var bounds: CGRect {