From 0823f9b2ed6dc4161548e5eab152b355c3657588 Mon Sep 17 00:00:00 2001 From: jrtxio Date: Wed, 16 Sep 2026 09:08:48 +0800 Subject: [PATCH] webview (macOS): exempt the process from App Nap while a window is open MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit macOS throttles/suspends 'idle' apps whose windows are fully occluded. For monitoring-style Glaze UIs (live charts, streaming logs, dashboards) that means the page's timers and rendering freeze as soon as another window covers the app — the backend keeps serving, the UI goes stale. ensure-app! now declares NSActivityUserInitiated via [NSProcessInfo beginActivityWithOptions:] once per process. A Glaze app with an open window is by definition user-initiated work, so the token is intentionally never ended. Failure to acquire the activity is non-fatal (best effort). Note: this removes the process-level suspension component. WebKit may still apply its own page-visibility throttling in some macOS versions; that behavior, if observed, is a documented platform limitation rather than an App Nap effect. glaze-test: 216 passed (includes the macOS WebView e2e suite). Fixes #2 --- glaze/webview/webview-macos.rkt | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/glaze/webview/webview-macos.rkt b/glaze/webview/webview-macos.rkt index 73b795e..2642e56 100644 --- a/glaze/webview/webview-macos.rkt +++ b/glaze/webview/webview-macos.rkt @@ -89,6 +89,7 @@ (import-class NSString NSNull NSApplication + NSProcessInfo NSMenu NSMenuItem NSWindow @@ -306,6 +307,27 @@ ;; Standard menus install once; re-running install-standard-menus! would ;; replace the main menu and wipe any custom menus set-menu! appended. (define standard-menus-installed? #f) +(define app-nap-disabled? #f) + +;; App Nap exemption. macOS throttles/suspends "idle" apps whose windows are +;; fully occluded, which freezes monitoring-style Glaze UIs (live charts, +;; log tails, streaming views) the moment another window covers them. Decla- +;; ring user-initiated activity for the process lifetime keeps the WebView +;; live in the background; a Glaze app with an open window IS user-initiated +;; work, so the token is intentionally never ended. Options value: +;; NSActivityUserInitiated = 0x00FFFFFF (mask includes the IdleSystemSleep +;; bit; per NSProcessInfo.h). +(define (disable-app-nap!) + (unless app-nap-disabled? + (set! app-nap-disabled? #t) + (with-handlers ([exn:fail? (lambda (_) (void))]) + (define pi (tell NSProcessInfo processInfo)) + (define token + (tell #:type _id pi beginActivityWithOptions: #:type _uint64 16777215 + reason: #:type _id (->nsstring "Glaze UI running"))) + ;; token kept forever; no endActivity counterpart by design + (void token)))) + (define (ensure-app!) (call-with-semaphore app-init-sema @@ -315,6 +337,7 @@ (unless standard-menus-installed? (install-standard-menus! app) (set! standard-menus-installed? #t)) + (disable-app-nap!) ;; macOS 14+ deprecates activateIgnoringOtherApps: in favor of -activate. (if (tell app respondsToSelector: #:type _SEL (selector activate)) (tellv app activate)