From ffd14a92752830b4ba5acaacd3668d96ea73026a Mon Sep 17 00:00:00 2001 From: jrtxio Date: Sun, 25 Oct 2026 10:38:05 +0800 Subject: [PATCH] fix(installer): stage the branded dmg background at the window's point size Current Finder draws the icon-view backdrop 1:1 at its native pixel size and never scales it to the window, so a 2x (1320x800) artwork rendered only its top-left quarter (issue #322). The packager now resamples the staged copy to the window's 660x400 point size with sips and verifies the result; products keep authoring 2x artwork for sharpness. The live DMG test gates the staged dimensions, and configuration.md documents the real behavior plus the title-bar and icon-label safe zones. --- docs/configuration.md | 2 +- rivet-cli/installer.rkt | 52 ++++++++++++++++++++++++++++++--- rivet/tests/macos-installer.rkt | 30 +++++++++++++++++-- 3 files changed, 77 insertions(+), 7 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 14238b44..95ae2a6c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -124,7 +124,7 @@ strings. Only declare languages the application itself already ships. - `linux-icon` — an optional project-relative `.png` file installed as the desktop icon (`/usr/share/pixmaps`, referenced by the generated `.desktop` entry) and used as the AppImage top-level icon. AppImage packaging fails closed without it; deb and rpm warn and install without a desktop icon when the key is omitted. - `linux-formats` — an optional subset of `("deb" "rpm" "appimage")` selecting which native Linux installer formats `raco rivet release` builds. The default builds all three; the signed tar.gz update payload is always produced regardless. - `macos-resource-files` — a list of unique project-relative files copied verbatim into the macOS bundle's `Contents/Resources` root before signing. The motivating consumer is `InternetAccessPolicy.plist`: Little Snitch-class firewalls read it from the Resources root and surface its per-host purpose and deny consequences to the user. Names collide fail closed against rivet-managed bundle files (the packaged icon). -- `dmg-background` — an optional project-relative `.png` used as the DMG window background. Declaring it switches `raco rivet release` to the branded drag-to-install layout: the volume carries an `/Applications` symlink and the DMG window gets a fixed 660x400 geometry with both icons placed. Render the PNG at 1320x800 (2x) so it stays sharp on Retina displays; Finder scales it to the window. Without the key the DMG stays a plain single-folder image. +- `dmg-background` — an optional project-relative `.png` used as the DMG window background. Declaring it switches `raco rivet release` to the branded drag-to-install layout: the volume carries an `/Applications` symlink and the DMG window gets a fixed 660x400 geometry with both icons placed. Render the PNG at 1320x800 (2x of the window) so it stays sharp on Retina displays; the aspect ratio must match the window, and the packager downsamples the staged copy to 660x400 because Finder draws the backdrop at native pixel size without scaling it (anything else crops the layout). Keep the bottom ~28pt of the artwork clear (the window bounds include the title bar) and leave the icon-label spans free. Without the key the DMG stays a plain single-folder image. - `windows-license` — an optional project-relative `.rtf` file shown on the MSI wizard's license page. Without the key, the release pipeline renders the repository `LICENSE` text (or `LICENSE.txt`/`COPYING`) into an RTF automatically; a project with neither ships a neutral notice pointing at the license that installs with the application. - `windows-installer-bitmaps` — an optional hash with `banner` and `dialog` values naming project-relative `.bmp` files for the MSI wizard (493x58 and 493x312 respectively). Unset values keep the wizard's built-in artwork. diff --git a/rivet-cli/installer.rkt b/rivet-cli/installer.rkt index 41516e51..fc78e9f9 100644 --- a/rivet-cli/installer.rkt +++ b/rivet-cli/installer.rkt @@ -324,9 +324,10 @@ (run! 'create-installer! xcrun "stapler" "staple" (path->string output))) output) -;; Branded DMG layout constants. The window geometry matches the generated -;; background artwork aspect ratio (products render 1320x800 PNGs for the -;; 660x400 window), and both icons sit on the horizontal midline. +;; Branded DMG layout constants. The window geometry defines the staged +;; background's point size (products may author 2x artwork for sharpness; +;; it is downsampled before staging), and both icons sit on the horizontal +;; midline. (define dmg-window-origin-x 200) (define dmg-window-origin-y 120) (define dmg-window-width 660) @@ -379,6 +380,25 @@ (when (file-exists? path) (delete-file path)) (when (directory-exists? path) (delete-directory/files path))) +;; `sips -g` answers one " pixelWidth: 1320" style line per requested +;; property; the dimensions decide whether the staged backdrop needs +;; resampling and verify the result afterwards. +(define (raster-pixel-size path sips) + (define output + (with-output-to-string + (lambda () + (run! 'create-installer! sips + "-g" "pixelWidth" "-g" "pixelHeight" + (path->string path))))) + (define width (regexp-match #px"pixelWidth:\\s*([0-9]+)" output)) + (define height (regexp-match #px"pixelHeight:\\s*([0-9]+)" output)) + (unless (and width height) + (error 'create-installer! + "could not read the dmg background pixel size" + "background" path)) + (list (string->number (cadr width)) + (string->number (cadr height)))) + ;; Commercial drag-to-install image: stage the volume contents (bundle, ;; /Applications link, hidden background asset), attach a read/write image, ;; let Finder size the window and place both icons, then convert to the @@ -415,7 +435,30 @@ (make-file-or-directory-link "/Applications" (build-path staging "Applications")) (make-directory* (build-path staging ".background")) - (copy-file background (build-path staging ".background" "background.png")) + (define staged-background (build-path staging ".background" "background.png")) + (copy-file background staged-background) + ;; Finder draws the backdrop 1:1 at its native pixel size — it does not + ;; scale it to the window, so a 2x artwork rendered only its top-left + ;; quarter and the bottom of the layout fell outside the view (issue + ;; #322). Stage the raster at the window's point size so what Finder + ;; draws matches the window exactly; products keep authoring 2x artwork + ;; for sharpness, and the artwork aspect must already match the window + ;; geometry (a mismatch resamples distorted rather than hiding layout). + (define sips (find-executable-path "sips")) + (unless sips + (error 'create-installer! "sips is required to stage the dmg background")) + (unless (equal? (raster-pixel-size staged-background sips) + (list dmg-window-width dmg-window-height)) + (run! 'create-installer! sips "-z" + (number->string dmg-window-height) + (number->string dmg-window-width) + (path->string staged-background)) + (unless (equal? (raster-pixel-size staged-background sips) + (list dmg-window-width dmg-window-height)) + (error 'create-installer! + "the staged dmg background did not resample to the window size" + "background" staged-background + "expected" (list dmg-window-width dmg-window-height)))) (delete-optional! rw-image) (run! 'create-installer! hdiutil "create" "-fs" "HFS+" "-volname" volume-name "-srcfolder" (path->string staging) @@ -507,4 +550,5 @@ ensure-wix-extensions! text->rtf dmg-style-applescript + raster-pixel-size create-macos-installer!)) diff --git a/rivet/tests/macos-installer.rkt b/rivet/tests/macos-installer.rkt index a410176e..0a526518 100644 --- a/rivet/tests/macos-installer.rkt +++ b/rivet/tests/macos-installer.rkt @@ -32,6 +32,24 @@ (dmg-style-applescript "Smoke \"App\"" "Smoke.app") "quotes in display names must be AppleScript-escaped") +;; --------------------------------------------------------------------------- +;; Staged background normalization (macOS: raster-pixel-size shells to sips) + +(module+ test + (when (equal? 'macosx (system-type 'os)) + (define sips (find-executable-path "sips")) + (when sips + (define one-by-one-png + (base64-decode + #"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==")) + (define probe (make-temporary-file "rivet-dmg-pixel-~a.png")) + (call-with-output-file probe + #:exists 'truncate/replace + (lambda (out) (write-bytes one-by-one-png out))) + (check-equal? (raster-pixel-size probe sips) '(1 1) + "the reader must report the PNG's own pixel size") + (delete-file probe)))) + ;; --------------------------------------------------------------------------- ;; Live branded DMG build (macOS hosts with Finder automation) @@ -93,8 +111,9 @@ (make-file-or-directory-link "Versions/Current/Racket" (build-path fw "Racket")) - ;; A 1x1 PNG is enough for the live run: Finder scales the background to - ;; the window bounds. + ;; A 1x1 PNG is enough for the live run: the packager resamples whatever + ;; it is given to the window's point size before staging (Finder draws + ;; the backdrop at native pixel size and never scales it — issue #322). (define tiny-png (base64-decode #"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==")) @@ -136,6 +155,13 @@ "the Applications shortcut must be a symlink") (check-true (file-exists? (build-path mnt ".background" "background.png")) "the background asset must ride in the hidden folder") + ;; The staged backdrop must be exactly the window's point size: Finder + ;; draws it 1:1 at native pixel size, so anything else crops the layout + ;; (issue #322). + (let ([staged (raster-pixel-size (build-path mnt ".background" "background.png") + (find-executable-path "sips"))]) + (check-equal? staged '(660 400) + "the staged background must match the window point size")) (check-true (file-exists? (build-path mnt ".DS_Store")) "the Finder styling must have written the icon layout")