Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

This change only queues the exposed region to be repainted on UIKit. #291

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
103 changes: 57 additions & 46 deletions Sources/SwiftTerm/Apple/AppleTerminalView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,11 @@ extension TerminalView {

search = SearchService (terminal: terminal)

#if os(macOS)
#if os(macOS)
needsDisplay = true
#else
#else
setNeedsDisplay(frame)
#endif
#endif
}

/// Returns the underlying terminal emulator that the `TerminalView` is a view for
Expand Down Expand Up @@ -534,25 +534,25 @@ extension TerminalView {
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)
}

// draw lines
#if os(iOS)
#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)
#else
#else
// On Mac, we are drawing the terminal buffer
let cellHeight = cellDimension.height
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

#endif
for row in firstRow...lastRow {
if row < 0 {
continue
Expand All @@ -575,22 +575,22 @@ extension TerminalView {
// Debug aid
// context.setFillColor(CGColor(red: 0, green: Double (row)/25.0, blue: 0, alpha: 1))
// context.fill([lineRect])

context.translateBy(x: 0, y: pivot)
context.scaleBy (x: 2, y: 2)
context.translateBy(x: 0, y: -pivot)

case .doubledTop:
context.saveGState()
let pivot = lineOrigin.y + cellDimension.height
let lineRect = CGRect (origin: CGPoint (x: 0, y: lineOrigin.y), size: CGSize (width: dirtyRect.width, height: cellDimension.height))

context.clip(to: [lineRect])

// Debug Aid
//context.setFillColor(CGColor(red: Double (row)/25.0, green: 0, blue: 0, alpha: 1))
//context.fill([lineRect])

context.translateBy(x: 0, y: pivot)
context.scaleBy (x: 2, y: 2)
context.translateBy(x: 0, y: -pivot)
Expand All @@ -599,85 +599,85 @@ extension TerminalView {
context.saveGState()
context.scaleBy (x: 2, y: 1)
}
#if false
#if false
// This optimization is useful, but only if we can get proper exposed regions
// and while it works most of the time with the BigSur change, there is still
// a case where we just get full exposes despite requesting only a line
// repro: fill 300 lines, then clear screen then repeatedly output commands
// that produce 3-5 lines of text: while we send AppKit the right boundary,
// AppKit still send everything.
// AppKit still send everything.
let lineRect = CGRect (origin: lineOrigin, size: CGSize (width: dirtyRect.width, height: cellDimension.height))

if !lineRect.intersects(dirtyRect) {
//print ("Skipping row \(row) because it does nto intersect")
continue
}
#endif
}
#endif
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)
let runAttributes = CTRunGetAttributes(run) as? [NSAttributedString.Key: Any] ?? [:]
let runFont = runAttributes[.font] as! TTFont

let runGlyphs = [CGGlyph](unsafeUninitializedCapacity: runGlyphsCount) { (bufferPointer, count) in
CTRunGetGlyphs(run, CFRange(), bufferPointer.baseAddress!)
count = runGlyphsCount
}

var positions = runGlyphs.enumerated().map { (i: Int, glyph: CGGlyph) -> CGPoint in
CGPoint(x: lineOrigin.x + (cellDimension.width * CGFloat(col + i)), y: lineOrigin.y + yOffset)
}

var backgroundColor: TTColor?
if runAttributes.keys.contains(.selectionBackgroundColor) {
backgroundColor = runAttributes[.selectionBackgroundColor] as? TTColor
} else if runAttributes.keys.contains(.backgroundColor) {
backgroundColor = runAttributes[.backgroundColor] as? TTColor
}

if let backgroundColor = backgroundColor {
context.saveGState ()

context.setShouldAntialias (false)
context.setLineCap (.square)
context.setLineWidth(0)
context.setFillColor(backgroundColor.cgColor)

let transform = CGAffineTransform (translationX: positions[0].x, y: 0)

var size = CGSize (width: CGFloat (cellDimension.width * CGFloat(runGlyphsCount)), height: cellDimension.height)
var origin: CGPoint = lineOrigin

#if (lastLineExtends)
#if (lastLineExtends)
// Stretch last col/row to full frame size.
// TODO: need apply this kind of fixup to selection too
if (row-terminal.buffer.yDisp) >= terminal.rows - 1 {
let missing = frame.height - (cellDimension.height + CGFloat(row) + 1)
size.height += missing
origin.y -= missing
}
#endif

#endif
if col + runGlyphsCount >= terminal.cols {
size.width += frame.width - size.width
}

let rect = CGRect (origin: origin, size: size)

#if os(macOS)
#if os(macOS)
rect.applying(transform).fill(using: .destinationOver)
#else
#else
context.fill(rect.applying(transform))
#endif
#endif
context.restoreGState()
}

nativeForegroundColor.set()

if runAttributes.keys.contains(.foregroundColor) {
let color = runAttributes[.foregroundColor] as! TTColor
let cgColor = color.cgColor
Expand All @@ -688,13 +688,13 @@ extension TerminalView {
}

CTFontDrawGlyphs(runFont, runGlyphs, &positions, positions.count, context)

// Draw other attributes
drawRunAttributes(runAttributes, glyphPositions: positions, in: context)

col += runGlyphsCount
}

// Render any sixel content last
if let images = lineInfo.images {
let rowBase = frame.height - (CGFloat(row) * cellDimension.height)
Expand Down Expand Up @@ -730,18 +730,27 @@ extension TerminalView {
nativeBackgroundColor.setFill()
context.fill ([box])
}
#elseif false
#else
if 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.
//
// NOTE: there is another reason, the caller needs to clear the region
// to avoid situations where the previous text is not painted over
// when we have a transparent background that does not clear the
// previous color before.

// Perhaps cast
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)
Expand Down Expand Up @@ -811,7 +820,7 @@ extension TerminalView {

terminal.clearUpdateRange ()

#if os(macOS)
#if os(macOS)
let baseLine = frame.height
var region = CGRect (x: 0,
y: baseLine - (cellDimension.height + CGFloat(rowEnd) * cellDimension.height),
Expand All @@ -826,11 +835,13 @@ extension TerminalView {
region = CGRect (x: 0, y: 0, width: frame.width, height: oh + oy)
}
setNeedsDisplay(region)
#else
// TODO iOS: need to update the code above, but will do that when I get some real
// life data being fed into it.
setNeedsDisplay(bounds)
#endif
#else
let region = CGRect (x: 0,
y: CGFloat (rowStart)*cellDimension.height+contentOffset.y,
width: frame.width,
height: CGFloat (rowEnd-rowStart+1) * cellDimension.height)
setNeedsDisplay(region)
#endif

pendingDisplay = false
updateDebugDisplay ()
Expand Down
10 changes: 7 additions & 3 deletions Sources/SwiftTerm/iOS/iOSTerminalView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -962,10 +962,14 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
return
}

// 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
// This is necessary because we might be re-rendering a
// part of the screen, and we might have a transparent
// background, so it is necessary to clear first.
nativeBackgroundColor.set ()
//context.clear (dirtyRect)

// If this works with the transparent background, that should work
// and it avoids the extra code in ApplTerminalView in the iOS case
context.fill ([dirtyRect])

// drawTerminalContents and CoreText expect the AppKit coordinate system
Expand Down
1 change: 1 addition & 0 deletions TerminalApp/MacTerminal/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ class ViewController: NSViewController, LocalProcessTerminalViewDelegate, NSUser
terminal = LocalProcessTerminalView(frame: view.frame)
zoomGesture = NSMagnificationGestureRecognizer(target: self, action: #selector(zoomGestureHandler))
terminal.addGestureRecognizer(zoomGesture!)
terminal.getTerminal().backgroundColor = SwiftTerm.Color(red: 0, green: 0x8000, blue: 0)
ViewController.lastTerminal = terminal
terminal.processDelegate = self
terminal.feed(text: "Welcome to SwiftTerm")
Expand Down
1 change: 1 addition & 0 deletions TerminalApp/iOSTerminal/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ class ViewController: UIViewController {
}

view.addSubview(tv)
tv.getTerminal().backgroundColor = SwiftTerm.Color.init(red: 0x8080, green: 0, blue: 0)
setupKeyboardMonitor()
tv.becomeFirstResponder()
self.tv.feed(text: "Welcome to SwiftTerm - connecting to my localhost\r\n\n")
Expand Down