summaryrefslogtreecommitdiff
path: root/src/macos/Sources/AppDelegate.swift
diff options
context:
space:
mode:
Diffstat (limited to 'src/macos/Sources/AppDelegate.swift')
-rw-r--r--src/macos/Sources/AppDelegate.swift57
1 files changed, 40 insertions, 17 deletions
diff --git a/src/macos/Sources/AppDelegate.swift b/src/macos/Sources/AppDelegate.swift
index 077367a4..0cecd5da 100644
--- a/src/macos/Sources/AppDelegate.swift
+++ b/src/macos/Sources/AppDelegate.swift
@@ -18,10 +18,9 @@ final class AppDelegate: NSObject, NSApplicationDelegate, PardesViewDelegate {
/// The blur behind the grid, shown only while the theme declares no
/// background of its own. See applyTheme.
private var backdrop: NSVisualEffectView!
- // One pump chain at a time. pump() re-arms itself while a theme transition
- // is in flight and every input pumps as well, so without this a burst of
- // keys during a fade would leave one 60 Hz chain per keystroke, all of them
- // ticking until the fade ended.
+ // One display-clock chain at a time. Input and pty notifications may all
+ // call pump(), but none advances animation; they only observe this flag and
+ // leave the already-scheduled frame alone.
private var pumpScheduled: Bool = false
// The core is a singleton with no "is it alive" query, and Finder can hand
// us documents before applicationDidFinishLaunching runs. Every entry point
@@ -48,9 +47,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate, PardesViewDelegate {
installMainMenu()
- // ponytail: 14pt, fixed. The SDL shell steps its font on Ctrl+/Ctrl-
- // (gui.zig); doing that here means re-measuring the view's metrics and
- // pushing a resize behind it, so it waits until the font has to move.
+ // Initial body size. PardesView owns later Ctrl+/Ctrl- changes and
+ // rebuilds both body and tagline metrics before publishing the resize.
view = PardesView(fontSize: defaultFontSize)
let want = NSSize(width: 1000, height: 700)
@@ -199,6 +197,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate, PardesViewDelegate {
// core compares the effective pixel viewport, not the resize event, so
// duplicate SIGWINCH-shaped notifications are already a no-op.
pardesViewDidResize(view)
+ // That pump also consumes a Font from startup config, if present. Only
+ // now is the face on screen final enough to seed Config's effective
+ // name and point size; later adopts and zooms report from wear().
+ view.reportEffectiveFont()
// Positional paths, after the first frame's worth of state exists.
// Anything starting with `-` is skipped rather than opened: the core
@@ -534,6 +536,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate, PardesViewDelegate {
// read bytes.
_ = pardes_tick()
view.needsDisplay = true
+ // TaglineSize is host-owned raster state just like Font, but unlike a
+ // body face change it does not resize the grid. Polling the one-byte
+ // core setting keeps startup config and live commands on one path.
+ view.adoptTaglinePercent(pardes_gui_tagline_font_percent())
// After the tick, because a `Theme` command runs inside it and the
// window has to follow the grid in the same frame rather than at the
// next launch.
@@ -571,19 +577,36 @@ final class AppDelegate: NSObject, NSApplicationDelegate, PardesViewDelegate {
NSApp.terminate(nil)
return
}
- // A theme transition is the only thing that moves on its own, and it is
- // ten 16 ms steps (animation.zig). Nothing else re-arms this, which is
- // the point of an event-driven host: an idle pardes costs no CPU, where
- // the SDL shell spins.
- if pardes_animating() && !pumpScheduled {
- pumpScheduled = true
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.016) {
- self.pumpScheduled = false
- self.pump()
- }
+ // Input and pty pumps only drain work. A frame-count animation advances
+ // on this 16 ms clock, and only after the sample dirtied above was
+ // actually presented. A burst of input cannot make it run fast, while
+ // an occluded/coalesced AppKit view cannot consume invisible samples.
+ scheduleAnimationFrame()
+ }
+
+ private func scheduleAnimationFrame() {
+ guard pardes_animating() && !pumpScheduled else { return }
+ let awaitingPresentation = view.presentationSerial
+ pumpScheduled = true
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.016) {
+ self.animationFrame(after: awaitingPresentation)
}
}
+ private func animationFrame(after awaitingPresentation: UInt64) {
+ pumpScheduled = false
+ guard view.presentationSerial != awaitingPresentation else {
+ // Keep the dirty sample pending. AppKit may have coalesced this
+ // draw or the window may be occluded; retry the clock without
+ // advancing until draw(_:) supplies the presentation permit.
+ view.needsDisplay = true
+ scheduleAnimationFrame()
+ return
+ }
+ _ = pardes_animation_tick()
+ pump()
+ }
+
func pardesViewDidResize(_ view: PardesView) {
let grid = view.gridSize
// cell_w/cell_h are physical pixels. The core hands them to the native