Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
fe3c6d0
test: cover terminal cell metrics across split reflow
austinywang Sep 16, 2026
6723ce0
test: reproduce lost resize completion and Retina metrics
austinywang Sep 16, 2026
cf83c2a
fix: reconcile terminal metrics and finish inactive pane resizes
austinywang Sep 16, 2026
5bbe5e7
test: yield actor execution while terminal geometry settles
austinywang Sep 16, 2026
961ec1a
test: initialize AppKit before pane fixture setup
austinywang Sep 17, 2026
c23d91b
test: read physical viewport rows and await divider completion
austinywang Sep 17, 2026
959f16c
test: inspect native grid spans for physical reflow
austinywang Sep 17, 2026
5209dd7
test: assert exported native cell widths directly
austinywang Sep 17, 2026
caba6aa
test: satisfy required deinit lint
austinywang Sep 19, 2026
dc8c649
Merge main and integrate metric invalidation with committed geometry
austinywang Sep 19, 2026
9f104a3
Merge remote-tracking branch 'origin/main' into issue-12381-split-ref…
austinywang Sep 21, 2026
80a9571
fix: restore test target membership after main merge
austinywang Sep 21, 2026
e1c70e2
fix: restore metrics test target membership
austinywang Sep 21, 2026
8f2fe4a
fix: restore app source membership after main merge
austinywang Sep 21, 2026
d2f5400
Merge remote-tracking branch 'origin/main' into issue-12381-split-ref…
austinywang Sep 25, 2026
32e8414
Merge remote-tracking branch 'origin/main' into issue-12381-split-ref…
austinywang Sep 25, 2026
7a36721
test: preserve logical markers through hidden pane reflow
austinywang Sep 25, 2026
1aa71cc
test: preserve soft-wrapped terminal markers
austinywang Sep 25, 2026
9f8760a
Merge remote-tracking branch 'origin/main' into issue-12381-split-ref…
austinywang Sep 25, 2026
db5d7a7
test: read reflow markers from the viewport
austinywang Sep 25, 2026
2001e75
Merge origin/main into issue-12381-split-reflow-metrics
austinywang Oct 2, 2026
3a713d6
Normalize Xcode project after main merge
austinywang Oct 2, 2026
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
21 changes: 21 additions & 0 deletions Sources/GhosttyNSView+CellMetrics.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import AppKit
import CmuxTerminal

extension GhosttyNSView {
/// Invalidates a queued metric snapshot by reading the current runtime.
/// Ghostty reports backing pixels; AppKit scrolling and hit testing use points.
@discardableResult
func synchronizeCellMetrics() -> Bool {
Comment thread
austinywang marked this conversation as resolved.
guard let size = terminalSurface?.cellSizePoints(),
size.width.isFinite, size.height.isFinite,
size.width > 0, size.height > 0,
cellSize != size else { return false }
cellSize = size
NotificationCenter.default.post(
name: .ghosttyDidUpdateCellSize,
object: self,
userInfo: [GhosttyNotificationKey.cellSize: size]
)
return true
}
}
6 changes: 3 additions & 3 deletions Sources/GhosttyTerminalView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5454,7 +5454,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations {
layer?.masksToBounds = true
let didChangeDrawable = applyDrawableGeometry(geometry)
let surfaceSizeChanged = terminalSurface.commitPaneGeometry(geometry)
return didChangeDrawable || surfaceSizeChanged
return synchronizeCellMetrics() || didChangeDrawable || surfaceSizeChanged

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: A queued GHOSTTY_ACTION_CELL_SIZE callback can overwrite this live-metric refresh with its captured size after the geometry commit. Change the callback to treat the payload as an invalidation and call synchronizeCellMetrics() instead of assigning it directly.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At Sources/GhosttyTerminalView.swift, line 5457:

<comment>A queued `GHOSTTY_ACTION_CELL_SIZE` callback can overwrite this live-metric refresh with its captured size after the geometry commit. Change the callback to treat the payload as an invalidation and call `synchronizeCellMetrics()` instead of assigning it directly.</comment>

<file context>
@@ -5454,7 +5454,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations {
         let didChangeDrawable = applyDrawableGeometry(geometry)
         let surfaceSizeChanged = terminalSurface.commitPaneGeometry(geometry)
-        return didChangeDrawable || surfaceSizeChanged
+        return synchronizeCellMetrics() || didChangeDrawable || surfaceSizeChanged
     }
 
</file context>

}

/// Re-applies the current pane size: the committed geometry for a
Expand All @@ -5468,7 +5468,7 @@ class GhosttyNSView: NSView, NSUserInterfaceValidations {
guard let geometry = terminalSurface.committedPaneGeometry else { return false }
let didChangeDrawable = applyDrawableGeometry(geometry)
let surfaceSizeChanged = terminalSurface.reapplyCommittedPaneGeometry()
return didChangeDrawable || surfaceSizeChanged
return synchronizeCellMetrics() || didChangeDrawable || surfaceSizeChanged
}
return commitOwnBounds()
}
Expand Down Expand Up @@ -13687,7 +13687,7 @@ final class GhosttySurfaceScrollView: NSView {
// publishes its inner frame here.
if surfaceView.paneGeometryIsPortalOwned {
paneGeometryPortal?.requestPaneGeometryCommit(for: self)
return false
return surfaceView.synchronizeCellMetrics()
}
return surfaceView.commitPaneGeometry(
size: CGSize(width: width, height: height),
Expand Down
16 changes: 16 additions & 0 deletions cmux.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -2120,6 +2120,7 @@
A11EAF000000000000000000 /* GhosttyNotificationDispatcherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A11EAF000000000000000001 /* GhosttyNotificationDispatcherTests.swift */; };
D78971000000000000000001 /* GhosttyNotificationScrollRuntimeBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78971000000000000000002 /* GhosttyNotificationScrollRuntimeBridge.swift */; };
09F6D65337EEB5988F365778 /* GhosttyNSView+Accessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C738D190FCB85202992C756 /* GhosttyNSView+Accessibility.swift */; };
123810000000000000000006 /* GhosttyNSView+CellMetrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123810000000000000000005 /* GhosttyNSView+CellMetrics.swift */; };
B88357000000000000000001 /* GhosttyNSView+ClipboardActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B88357000000000000000002 /* GhosttyNSView+ClipboardActions.swift */; };
B88357000000000000000003 /* GhosttyNSView+ClipboardInputSequencing.swift in Sources */ = {isa = PBXBuildFile; fileRef = B88357000000000000000004 /* GhosttyNSView+ClipboardInputSequencing.swift */; };
3436831626068CC556129EB3 /* GhosttyNSView+CodexActionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9FA91EF89C03A74979E6EF7 /* GhosttyNSView+CodexActionCommand.swift */; };
Expand Down Expand Up @@ -3810,6 +3811,9 @@
C0DE6B420000000000000001 /* TerminalPanelTextBoxState.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0DE6B420000000000000002 /* TerminalPanelTextBoxState.swift */; };
6819AF60AEA6455DB57CC7FE /* TerminalPanelUnavailableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 811B03E4004A4D1FB6650158 /* TerminalPanelUnavailableView.swift */; };
A5001403 /* TerminalPanelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001413 /* TerminalPanelView.swift */; };
123810000000000000000002 /* TerminalPaneMetricInvalidationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123810000000000000000001 /* TerminalPaneMetricInvalidationTests.swift */; };
123810000000000000000004 /* TerminalPaneMetricsFixture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123810000000000000000003 /* TerminalPaneMetricsFixture.swift */; };
123810000000000000000008 /* TerminalPaneMetricsWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 123810000000000000000007 /* TerminalPaneMetricsWindow.swift */; };
B88351000000000000000001 /* TerminalPasteboardReadRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B88351000000000000000002 /* TerminalPasteboardReadRequest.swift */; };
B8835B000000000000000003 /* TerminalPasteFailureNotice.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8835B000000000000000004 /* TerminalPasteFailureNotice.swift */; };
B8835B000000000000000005 /* TerminalPasteFailureNoticePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8835B000000000000000006 /* TerminalPasteFailureNoticePresenter.swift */; };
Expand Down Expand Up @@ -6434,6 +6438,7 @@
A11EAF000000000000000001 /* GhosttyNotificationDispatcherTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GhosttyNotificationDispatcherTests.swift; sourceTree = "<group>"; };
D78971000000000000000002 /* GhosttyNotificationScrollRuntimeBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GhosttyNotificationScrollRuntimeBridge.swift; sourceTree = "<group>"; };
2C738D190FCB85202992C756 /* GhosttyNSView+Accessibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GhosttyNSView+Accessibility.swift"; sourceTree = "<group>"; };
123810000000000000000005 /* GhosttyNSView+CellMetrics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GhosttyNSView+CellMetrics.swift"; sourceTree = "<group>"; };
B88357000000000000000002 /* GhosttyNSView+ClipboardActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GhosttyNSView+ClipboardActions.swift"; sourceTree = "<group>"; };
B88357000000000000000004 /* GhosttyNSView+ClipboardInputSequencing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GhosttyNSView+ClipboardInputSequencing.swift"; sourceTree = "<group>"; };
A9FA91EF89C03A74979E6EF7 /* GhosttyNSView+CodexActionCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GhosttyNSView+CodexActionCommand.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -8100,6 +8105,9 @@
C0DE6B420000000000000002 /* TerminalPanelTextBoxState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Panels/TerminalPanelTextBoxState.swift; sourceTree = "<group>"; };
811B03E4004A4D1FB6650158 /* TerminalPanelUnavailableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Panels/TerminalPanelUnavailableView.swift; sourceTree = "<group>"; };
A5001413 /* TerminalPanelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Panels/TerminalPanelView.swift; sourceTree = "<group>"; };
123810000000000000000001 /* TerminalPaneMetricInvalidationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalPaneMetricInvalidationTests.swift; sourceTree = "<group>"; };
123810000000000000000003 /* TerminalPaneMetricsFixture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalPaneMetricsFixture.swift; sourceTree = "<group>"; };
123810000000000000000007 /* TerminalPaneMetricsWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalPaneMetricsWindow.swift; sourceTree = "<group>"; };
B88351000000000000000002 /* TerminalPasteboardReadRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalPasteboardReadRequest.swift; sourceTree = "<group>"; };
B8835B000000000000000004 /* TerminalPasteFailureNotice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalPasteFailureNotice.swift; sourceTree = "<group>"; };
B8835B000000000000000006 /* TerminalPasteFailureNoticePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalPasteFailureNoticePresenter.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -10301,6 +10309,7 @@
A9E88F76F64B4E908DD1CC4F /* GhosttyHostActionHandler.swift */,
5A23CB32F1DB4F76A94E7328 /* GhosttyApp+KeybindOverrides.swift */,
A5001015 /* GhosttyTerminalView.swift */,
123810000000000000000005 /* GhosttyNSView+CellMetrics.swift */,
126570000000000000000007 /* GhosttyTerminalView+Representable.swift */,
030176C8103741F8BDD27F26 /* CloudTerminalReconnectOverlayView.swift */,
DF976CA511F242519ADA8204 /* CloudTerminalOverlayCoordinator.swift */,
Expand Down Expand Up @@ -12312,6 +12321,9 @@
C12758000000000000000009 /* PlainPastePTYFixture.swift */,
C12758000000000000000007 /* TerminalPlainTextPastePTYTests.swift */,
D7AB34400000000000000004 /* GhosttyTerminalViewVisibilityPolicyTests.swift */,
123810000000000000000003 /* TerminalPaneMetricsFixture.swift */,
123810000000000000000007 /* TerminalPaneMetricsWindow.swift */,
123810000000000000000001 /* TerminalPaneMetricInvalidationTests.swift */,
5B6E489A561B4E319CE3331B /* TerminalPortalTransitionAttributionTests.swift */,
776C2DFD963A4F6E87A10FDA /* TerminalWindowPortalExternalGeometrySyncTests.swift */,
60D3266B6D154D5C9F14286E /* TerminalPortalReconciliationReentrancyTests.swift */,
Expand Down Expand Up @@ -14962,6 +14974,7 @@
F0A110070000000000000001 /* GhosttyMouseSessionLedgerSurfaceIdentity.swift in Sources */,
D78971000000000000000001 /* GhosttyNotificationScrollRuntimeBridge.swift in Sources */,
09F6D65337EEB5988F365778 /* GhosttyNSView+Accessibility.swift in Sources */,
123810000000000000000006 /* GhosttyNSView+CellMetrics.swift in Sources */,
B88357000000000000000001 /* GhosttyNSView+ClipboardActions.swift in Sources */,
B88357000000000000000003 /* GhosttyNSView+ClipboardInputSequencing.swift in Sources */,
3436831626068CC556129EB3 /* GhosttyNSView+CodexActionCommand.swift in Sources */,
Expand Down Expand Up @@ -17942,6 +17955,9 @@
A5E380700000000000000001 /* TerminalNotificationSocketActionTests.swift in Sources */,
11189010A1B2C3D4E5F60718 /* TerminalNotificationSocketAttributionTests.swift in Sources */,
596100000000000000000007 /* TerminalNotificationStore+NativeNotificationDeliveryTesting.swift in Sources */,
123810000000000000000002 /* TerminalPaneMetricInvalidationTests.swift in Sources */,
123810000000000000000004 /* TerminalPaneMetricsFixture.swift in Sources */,
123810000000000000000008 /* TerminalPaneMetricsWindow.swift in Sources */,
4E96608F6F558E792BE96C3B /* TerminalPasteFailureNoticeTests.swift in Sources */,
B12715000000000000000001 /* TerminalPastePreparationReapingTests.swift in Sources */,
C12758000000000000000010 /* TerminalPasteWorkerStartupBenchmark.swift in Sources */,
Expand Down
170 changes: 170 additions & 0 deletions cmuxTests/TerminalPaneMetricInvalidationTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import AppKit
import CmuxTerminal
import GhosttyKit
import Testing

#if canImport(cmux_DEV)
@testable import cmux_DEV
#elseif canImport(cmux)
@testable import cmux
#endif

@MainActor
@Suite(.serialized)
struct TerminalPaneMetricInvalidationTests {
@Test(arguments: [CGFloat(1), CGFloat(2)])
func fontCallbacksUseCurrentLogicalCellSize(backingScale: CGFloat) async throws {
let fixture = try await TerminalPaneMetricsFixture(backingScale: backingScale)
defer { fixture.tearDown() }
try await fixture.bind()
#expect(fixture.surface.performInternalBindingAction("set_font_size:24"))
#expect(fixture.surface.performInternalBindingAction("set_font_size:13"))
try await fixture.waitUntil("logical cell metrics") {
fixture.hosted.surfaceView.cellSize == fixture.surface.cellSizePoints()
}
let sample = try #require(fixture.surface.rawSizingSample())
#expect(fixture.hosted.surfaceView.cellSize.width == CGFloat(sample.cellWidthPx) / backingScale)
#expect(fixture.hosted.surfaceView.cellSize.height == CGFloat(sample.cellHeightPx) / backingScale)
}

@Test(arguments: [CGFloat(1), CGFloat(2)])
func hiddenOutputReflowsThroughDividerDragAndSplitClose(backingScale: CGFloat) async throws {
let fixture = try await TerminalPaneMetricsFixture(backingScale: backingScale)
defer { fixture.tearDown() }
// The original #12381 path: output arrives before the split is shown.
try fixture.writeRows()
try await fixture.bind()
let originalCell = try #require(fixture.surface.cellSizePoints())
let originalFont = ghostty_surface_font_size(try #require(fixture.surface.surface))
for width: CGFloat in [280, 620, 360] {
try await fixture.moveDivider(to: width)
try assertGridAndText(fixture, cell: originalCell, font: originalFont)
}
try await fixture.closeSibling()
try assertGridAndText(fixture, cell: originalCell, font: originalFont)
}

@Test func paneGeometryInvalidatesStaleLargerCellMetrics() async throws {
let fixture = try await TerminalPaneMetricsFixture()
defer { fixture.tearDown() }
try await fixture.bind()
let expected = try #require(fixture.surface.cellSizePoints())
// Simulate a delayed metric notification from the previous layout.
// Repair must come from each pane geometry boundary, with no window
// resize, config reload, or font-size mutation.
for closeSibling in [false, true] {
fixture.hosted.surfaceView.cellSize = CGSize(width: expected.width * 2, height: expected.height * 2)
if closeSibling { try await fixture.closeSibling() }
else { try await fixture.moveDivider(to: 340) }
#expect(fixture.hosted.surfaceView.cellSize == expected)
}
}

@Test func sameFrameReconciliationRepairsMetricCache() async throws {
let fixture = try await TerminalPaneMetricsFixture()
defer { fixture.tearDown() }
try await fixture.bind()
let expected = try #require(fixture.surface.cellSizePoints())
let frame = fixture.hosted.frame
fixture.hosted.surfaceView.cellSize = CGSize(width: expected.width * 2, height: expected.height * 2)
_ = fixture.hosted.reconcileGeometryNow()
#expect(fixture.hosted.frame == frame)
#expect(fixture.hosted.surfaceView.cellSize == expected)
}

@Test func splitCloseRecoversAfterNativeResizeEndCallbackWasLost() async throws {
let fixture = try await TerminalPaneMetricsFixture()
defer { fixture.tearDown() }
try await fixture.bind()
let portal = try #require(TerminalWindowPortalRegistry.portalsByWindowId[ObjectIdentifier(fixture.window)])
portal.isWindowLiveResizeActiveOverrideForTesting = true
defer { portal.isWindowLiveResizeActiveOverrideForTesting = false }
fixture.split.setPosition(320, ofDividerAt: 0)
TerminalWindowPortalRegistry.synchronizeForAnchor(fixture.anchor, syncLayout: false)
#expect(fixture.surface.committedPaneGeometry?.phase == .interactive)
portal.isWindowLiveResizeActiveOverrideForTesting = false
// No window notification: only the surviving pane's layout changes.
try await fixture.closeSibling()
#expect(fixture.surface.committedPaneGeometry?.phase == .settled)
}

@Test func activeDividerDoesNotFinishWhenNativeWindowResizeIsInactive() async throws {
let fixture = try await TerminalPaneMetricsFixture()
defer { fixture.tearDown() }
try await fixture.bind()
TerminalWindowPortalRegistry.beginInteractiveGeometryResize(in: fixture.window)
var dividerActive = true
defer {
if dividerActive { TerminalWindowPortalRegistry.endInteractiveGeometryResize(in: fixture.window) }
}
fixture.split.setPosition(320, ofDividerAt: 0)
TerminalWindowPortalRegistry.synchronizeExternalGeometryNow(for: fixture.window)
#expect(fixture.surface.committedPaneGeometry?.phase == .interactive)
TerminalWindowPortalRegistry.endInteractiveGeometryResize(in: fixture.window)
dividerActive = false
try await fixture.settle()
#expect(fixture.surface.committedPaneGeometry?.phase == .settled)
}

private func assertGridAndText(_ fixture: TerminalPaneMetricsFixture, cell: CGSize, font: Float) throws {
let sample = try #require(fixture.surface.rawSizingSample())
let runtime = try #require(fixture.surface.surface)
var grid = ghostty_surface_grid_metrics_s()
#expect(ghostty_surface_grid_metrics(runtime, &grid))
#expect(Int(grid.columns) == sample.columns)
#expect(Int(grid.rows) == sample.rows)
#expect(fixture.surface.cellSizePoints() == cell)
#expect(fixture.hosted.surfaceView.cellSize == cell)
#expect(ghostty_surface_font_size(runtime) == font)
// Text selection unwraps soft-wrapped lines even for rectangles.
// Inspect the native grid export, including each span's cell extent.
let rendered = try #require(fixture.surface.mobileRenderGridFrame(stateSeq: 0, includeTheme: false))
#expect(rendered.frame.columns == sample.columns)
#expect(rendered.frame.rows == sample.rows)
for span in rendered.frame.rowSpans {
let cellWidth = try #require(span.cellWidth)
#expect(span.row < sample.rows)
#expect(span.column + cellWidth <= sample.columns)
}
#expect(rendered.rows.allSatisfy { $0.count <= sample.columns })
// The native render-grid export is the physical reflow oracle above,
// but it can omit soft-wrapped text while a hidden pane is settling.
// Ghostty's direct reader preserves those logical lines and their
// markers, which is the behavior this regression path needs to prove.
let text = try readSurfaceText(fixture)
let compact = text.filter { !$0.isWhitespace }
for index in 1...4 {
let expected = "R\(index)" + String(repeating: "=", count: 74) + "END\(index)"
#expect(compact.components(separatedBy: expected).count == 2)
#expect(compact.components(separatedBy: "R\(index)").count == 2)
#expect(compact.components(separatedBy: "END\(index)").count == 2)
}
}

private func readSurfaceText(_ fixture: TerminalPaneMetricsFixture) throws -> String {
let runtime = try #require(fixture.surface.surface)
let selection = ghostty_selection_s(
top_left: ghostty_point_s(
tag: GHOSTTY_POINT_VIEWPORT,
coord: GHOSTTY_POINT_COORD_TOP_LEFT,
x: 0,
y: 0
),
bottom_right: ghostty_point_s(
tag: GHOSTTY_POINT_VIEWPORT,
coord: GHOSTTY_POINT_COORD_BOTTOM_RIGHT,
x: 0,
y: 0
),
rectangle: true
)
var text = ghostty_text_s()
guard ghostty_surface_read_text(runtime, selection, &text) else { return "" }
defer { ghostty_surface_free_text(runtime, &text) }
guard let pointer = text.text, text.text_len > 0 else { return "" }
return String(
decoding: Data(bytes: pointer, count: Int(text.text_len)),
as: UTF8.self
)
}
}
Loading
Loading