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
33 changes: 24 additions & 9 deletions Sources/SwiftTerm/Apple/AppleTerminalView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,6 @@ extension TerminalView {

public func synchronizedOutputChanged (source: Terminal, active: Bool)
{
SyncDebug.log("delegate active=\(active)")
if !active {
updateScroller()
queuePendingDisplay()
Expand Down Expand Up @@ -1613,12 +1612,10 @@ extension TerminalView {
{
defer { pendingDisplay = false }
if terminal.synchronizedOutputActive {
SyncDebug.log("paint-blocked sync=true")
return
}
updateCursorPosition()
guard let (rowStart, rowEnd) = terminal.getUpdateRange () else {
SyncDebug.log("paint-norange (cursor-only)")
if notifyUpdateChanges {
let buffer = terminal.displayBuffer
let y = buffer.yDisp+buffer.y
Expand Down Expand Up @@ -1646,7 +1643,6 @@ extension TerminalView {
terminalDelegate?.rangeChanged (source: self, startY: rowStart, endY: rowEnd)
}

SyncDebug.log("paint rows=\(rowStart)-\(rowEnd)")
terminal.clearUpdateRange ()

#if os(macOS)
Expand Down Expand Up @@ -1775,7 +1771,6 @@ extension TerminalView {
func queuePendingDisplay ()
{
if terminal.synchronizedOutputActive {
SyncDebug.log("queue-blocked sync=true")
return
}
// throttle
Expand All @@ -1784,12 +1779,10 @@ extension TerminalView {
// let fps30 = 16670000*2
let fpsDelay = fps60
pendingDisplay = true
SyncDebug.log("queue-scheduled (+16.67ms)")
DispatchQueue.main.asyncAfter(
deadline: DispatchTime (uptimeNanoseconds: DispatchTime.now().uptimeNanoseconds + UInt64 (fpsDelay)),
execute: updateDisplay)
} else {
SyncDebug.log("queue-noop (already pending)")
}
}

Expand Down Expand Up @@ -1987,11 +1980,32 @@ extension TerminalView {

func feedFinish ()
{
SyncDebug.log("feedFinish sync=\(terminal.synchronizedOutputActive)")
suspendDisplayUpdates ()
if shouldDisplayImmediatelyAfterUserInput() {
displayImmediately()
return
}
queuePendingDisplay()
}


private func shouldDisplayImmediatelyAfterUserInput() -> Bool {
guard !terminal.synchronizedOutputActive else { return false }
guard lastUserInputUptimeNs > 0 else { return false }
let now = DispatchTime.now().uptimeNanoseconds
guard now >= lastUserInputUptimeNs else { return false }
return now - lastUserInputUptimeNs <= interactiveInputDisplayWindowNs
}

private func displayImmediately() {
guard !Thread.isMainThread else {
updateDisplay()
return
}
DispatchQueue.main.async { [weak self] in
self?.updateDisplay()
}
}

/// Sends data to the terminal emulator for interpretation, this can be invoked from a background thread
public func feed (byteArray: ArraySlice<UInt8>)
{
Expand Down Expand Up @@ -2037,6 +2051,7 @@ extension TerminalView {
*/
public func send(data: ArraySlice<UInt8>)
{
lastUserInputUptimeNs = DispatchTime.now().uptimeNanoseconds
ensureCaretIsVisible ()
#if os(iOS) || os(visionOS)
if TerminalView.textInputDebugEnabled {
Expand Down
4 changes: 4 additions & 0 deletions Sources/SwiftTerm/Mac/MacTerminalView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ open class TerminalView: NSView, NSTextInputClient, NSUserInterfaceValidations,
private var findBarOptions: SearchOptions = SearchOptions()
var debug: TerminalDebugView?
var pendingDisplay: Bool = false
/// Output received shortly after local input is likely echo or prompt redraw;
/// render it without the 16.67ms frame-rate throttle so typing feels responsive.
var lastUserInputUptimeNs: UInt64 = 0
let interactiveInputDisplayWindowNs: UInt64 = 150_000_000
#if canImport(MetalKit)
var metalView: MTKView?
var metalRenderer: MetalTerminalRenderer?
Expand Down
4 changes: 0 additions & 4 deletions Sources/SwiftTerm/Terminal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5548,7 +5548,6 @@ open class Terminal {
private func beginSynchronizedOutput ()
{
let wasActive = synchronizedOutputActive
SyncDebug.log("BSU wasActive=\(wasActive)")
synchronizedOutputActive = true
scheduleSynchronizedOutputTimeout()
if !wasActive {
Expand All @@ -5559,10 +5558,8 @@ open class Terminal {
private func endSynchronizedOutput ()
{
guard synchronizedOutputActive else {
SyncDebug.log("ESU ignored (not active)")
return
}
SyncDebug.log("ESU")
synchronizedOutputActive = false
synchronizedOutputTimeoutItem?.cancel()
synchronizedOutputTimeoutItem = nil
Expand All @@ -5577,7 +5574,6 @@ open class Terminal {
guard let self, self.synchronizedOutputActive else {
return
}
SyncDebug.log("safety-timer-fired (missing ESU)")
self.endSynchronizedOutput()
}
synchronizedOutputTimeoutItem = workItem
Expand Down
4 changes: 4 additions & 0 deletions Sources/SwiftTerm/iOS/iOSTerminalView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ open class TerminalView: UIScrollView, UITextInputTraits, UIKeyInput, UIScrollVi
var search: SearchService!
var debug: UIView?
var pendingDisplay: Bool = false
/// Output received shortly after local input is likely echo or prompt redraw;
/// render it without the 16.67ms frame-rate throttle so typing feels responsive.
var lastUserInputUptimeNs: UInt64 = 0
let interactiveInputDisplayWindowNs: UInt64 = 150_000_000
#if canImport(MetalKit)
var metalView: MTKView?
var metalRenderer: MetalTerminalRenderer?
Expand Down
Loading