summaryrefslogtreecommitdiff
path: root/test/macos_e2e.swift
diff options
context:
space:
mode:
Diffstat (limited to 'test/macos_e2e.swift')
-rw-r--r--test/macos_e2e.swift29
1 files changed, 29 insertions, 0 deletions
diff --git a/test/macos_e2e.swift b/test/macos_e2e.swift
index c076aebb..800e2d43 100644
--- a/test/macos_e2e.swift
+++ b/test/macos_e2e.swift
@@ -95,6 +95,8 @@ private final class Driver: PardesViewDelegate {
private var observer: NSObjectProtocol?
private var output = ""
private var nextAnimationFrame: Date?
+ /// Where present() puts the pixels nobody reads.
+ private var presentationRep: NSBitmapImageRep?
/// The script's own file name, for error messages.
private let scriptName: String
@@ -219,6 +221,7 @@ private final class Driver: PardesViewDelegate {
window?.contentView = nil
window = nil
view = nil
+ presentationRep = nil
host.clipboard = nil
host.woke = false
}
@@ -336,6 +339,10 @@ private final class Driver: PardesViewDelegate {
private func readFrame() -> Frame {
let count = pardes_frame()
+ // The core has produced a frame; presenting it is this harness's half of
+ // the contract, and it has to happen AFTER the render rather than before
+ // — see present().
+ defer { present() }
let cols = Int(pardes_frame_cols())
let rows = Int(pardes_frame_rows())
// -1 means hidden. Clamped to 0 rather than printed, which is what
@@ -396,6 +403,28 @@ private final class Driver: PardesViewDelegate {
options: .regularExpression)
}
+ /// Put the frame readFrame() just produced on screen — for a window that has
+ /// no screen, into a bitmap. draw(_:) is the only caller of
+ /// pardes_frame_presented, and this window is deliberately never ordered
+ /// front, so without this nothing here would ever present anything: the core
+ /// would owe a presentation for the rest of the script and correctly hold
+ /// every pointer gesture after the first layout change inert, addressing
+ /// pixels no backend had shown. The app pays the same debt on every pump
+ /// (AppDelegate.pump marks the view, AppKit's display cycle draws it).
+ ///
+ /// The rep is kept and reused rather than allocated per frame: this runs on
+ /// every poll slice, and cacheDisplay only needs somewhere to put pixels
+ /// nobody reads. `draw` makes its own, because it reads them.
+ private func present() {
+ guard let view, view.bounds.width >= 1, view.bounds.height >= 1 else { return }
+ let bounds = view.bounds
+ if presentationRep?.size != bounds.size {
+ presentationRep = view.bitmapImageRepForCachingDisplay(in: bounds)
+ }
+ guard let rep = presentationRep else { return }
+ view.cacheDisplay(in: bounds, to: rep)
+ }
+
/// Render the view the way AppKit would and prove the CoreText pass put
/// something on the screen. `snap` reads the core's cell buffer and would be
/// perfectly happy with a draw(_:) that returned on its first line; this is