mirror of
https://github.com/mmacierzanka/second-display.git
synced 2026-11-05 04:31:46 +01:00
Style the DMG: dmgbuild layout, background, large icons
Layout is written by dmgbuild (Config/dmg/settings.py) instead of Finder AppleScript, which proved unreliable. Background is generated by scripts/make-dmg-background.swift. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
313be025d6
commit
6273a5b8c5
6 files changed
+68
-10
No files matched your search
@@ -16,7 +16,7 @@ A native macOS app that shows video from a capture device (camera / capture card
|
||||
`main.swift` (entry point), `App.swift` (state, window, lifecycle), `Capture.swift` (devices, formats, hot-plug), `Menu.swift` (menus and actions), `Power.swift` (sleep prevention), `Preferences.swift` (UserDefaults-backed settings).
|
||||
- `Config/Info.plist`, `Config/SecondDisplay.entitlements`, `Config/dmg/` – version, permissions (camera, microphone; hardened runtime).
|
||||
- `Resources/` – `AppIcon.icon` (Icon Composer, Liquid Glass; compiled by `actool`), `{en,pl}.lproj` (`Localizable.strings`, `InfoPlist.strings`), `uninstall.sh` (copied into the bundle; removes the app, preferences, caches, TCC permissions). Bundle ID: `com.mmacierzanka.seconddisplay`.
|
||||
- `scripts/check-strings.sh` – en/pl translation consistency.
|
||||
- `scripts/check-strings.sh` – en/pl translation consistency. `scripts/make-dmg-background.swift` regenerates `Config/dmg/background.png`; keep its size and icon coordinates in sync with `Config/dmg/settings.py` (dmgbuild layout, installed into `build/venv` on first `make dmg`).
|
||||
- `Makefile` – `make` (builds `build/SecondDisplay.app`), `run`, `check`, `install`, `uninstall`, `dmg`, `clean`, `help`. Builds are universal (arm64 + x86_64, macOS 26+). `make dmg` makes an un-notarized DMG with `Config/dmg/Read Me First.txt`. `SIGN_ID` defaults to ad-hoc (`-`).
|
||||
|
||||
## Conventions
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 181 KiB |
@@ -0,0 +1,23 @@
|
||||
# dmgbuild settings: writes the Finder window layout directly (no Finder/AppleScript needed).
|
||||
# Keep coordinates in sync with scripts/make-dmg-background.swift.
|
||||
app = defines['app'] # noqa: F821 (injected by dmgbuild -D)
|
||||
|
||||
format = 'UDZO'
|
||||
filesystem = 'HFS+'
|
||||
files = [app, 'Config/dmg/Read Me First.txt']
|
||||
symlinks = {'Applications': '/Applications'}
|
||||
background = 'Config/dmg/background.png'
|
||||
|
||||
window_rect = ((200, 120), (726, 720))
|
||||
default_view = 'icon-view'
|
||||
show_toolbar = False
|
||||
show_status_bar = False
|
||||
show_sidebar = False
|
||||
icon_size = 128
|
||||
text_size = 13
|
||||
arrange_by = None
|
||||
icon_locations = {
|
||||
'SecondDisplay.app': (198, 195),
|
||||
'Applications': (529, 195),
|
||||
'Read Me First.txt': (363, 435),
|
||||
}
|
||||
@@ -7,6 +7,7 @@ BUILDNUM := $(shell git rev-list --count HEAD 2>/dev/null || echo 1)
|
||||
|
||||
VERSION := $(shell /usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" Config/Info.plist)
|
||||
DMG := build/SecondDisplay-$(VERSION).dmg
|
||||
VOLNAME ?= Second Display
|
||||
|
||||
.PHONY: build run clean install uninstall check dmg help
|
||||
build: $(APP) ## Build build/SecondDisplay.app
|
||||
@@ -42,14 +43,15 @@ check: build ## Lint sources, plists, strings and the built bundle
|
||||
scripts/check-strings.sh
|
||||
codesign --verify --strict $(APP)
|
||||
|
||||
dmg: build ## Build a distributable disk image (not notarized)
|
||||
rm -rf build/dmg $(DMG) && mkdir build/dmg
|
||||
cp -R $(APP) build/dmg/
|
||||
ln -s /Applications build/dmg/Applications
|
||||
cp "Config/dmg/Read Me First.txt" build/dmg/
|
||||
hdiutil create -volname "Second Display" -srcfolder build/dmg -ov -format UDZO $(DMG)
|
||||
hdiutil verify $(DMG)
|
||||
rm -rf build/dmg
|
||||
DMGBUILD := build/venv/bin/dmgbuild
|
||||
|
||||
$(DMGBUILD):
|
||||
python3 -m venv build/venv && build/venv/bin/pip install -q dmgbuild
|
||||
|
||||
dmg: build $(DMGBUILD) ## Build a distributable disk image (not notarized)
|
||||
rm -f $(DMG)
|
||||
$(DMGBUILD) -s Config/dmg/settings.py -D app=$(APP) "$(VOLNAME)" $(DMG)
|
||||
hdiutil verify $(DMG) | tail -1
|
||||
|
||||
help: ## List targets
|
||||
@grep -E '^[a-z]+:.*##' Makefile | sed 's/:.*##/ -/'
|
||||
@@ -29,7 +29,7 @@ On first launch macOS asks for camera and microphone access — allow both. You
|
||||
If you received a prebuilt copy (the `.dmg`), drag the app to Applications. It is not notarized, so macOS blocks it on first launch: allow it once in System Settings → Privacy & Security → "Open Anyway", or run `xattr -dr com.apple.quarantine /Applications/SecondDisplay.app`. The same steps are in `Read Me First.txt` inside the disk image.
|
||||
|
||||
## Releasing a DMG
|
||||
`make dmg` builds a universal (arm64 + x86_64) app and packs it with an `Applications` shortcut and `Read Me First.txt` (source: `Config/dmg/`). The disk image is not signed with a Developer ID or notarized; the instructions in it explain how recipients allow it. Bump the version in `Config/Info.plist` before releasing and tag it (`git tag -a vX.Y.Z -m vX.Y.Z`).
|
||||
`make dmg` builds a universal (arm64 + x86_64) app and packs it with an `Applications` shortcut and `Read Me First.txt` (source: `Config/dmg/`) in a styled Finder window: large icons and a background with an arrow (`Config/dmg/background.png`, generated by `scripts/make-dmg-background.swift`). The layout is written directly into the image by [dmgbuild](https://github.com/dmgbuild/dmgbuild) (settings in `Config/dmg/settings.py`), so no Finder automation is needed. The first `make dmg` installs it into `build/venv` (needs `python3` and network access). The disk image is not signed with a Developer ID or notarized; the instructions in it explain how recipients allow it. Bump the version in `Config/Info.plist` before releasing and tag it (`git tag -a vX.Y.Z -m vX.Y.Z`).
|
||||
|
||||
## Usage
|
||||
Menus at the top of the screen: **Video** (device, format), **Audio** (source or off), **Options** (prevent display sleep, start in full screen, always on top, mute ⇧⌘M, scaling), **Window** (tiling, full screen ⌃⌘F).
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// Usage: swift scripts/make-dmg-background.swift -> writes Config/dmg/background.png (727x626 pt @2x)
|
||||
import AppKit
|
||||
|
||||
let w = 727.0, h = 626.0, scale = 2.0
|
||||
let rep = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(w * scale), pixelsHigh: Int(h * scale), bitsPerSample: 8,
|
||||
samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .deviceRGB, bytesPerRow: 0, bitsPerPixel: 0)!
|
||||
rep.size = NSSize(width: w, height: h) // 144 dpi, so Finder shows it at 727x626 pt
|
||||
NSGraphicsContext.saveGraphicsState()
|
||||
NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: rep)
|
||||
|
||||
NSGradient(starting: NSColor(red: 0.93, green: 0.95, blue: 0.99, alpha: 1), ending: NSColor(red: 0.80, green: 0.86, blue: 0.96, alpha: 1))!
|
||||
.draw(in: NSRect(x: 0, y: 0, width: w, height: h), angle: -90)
|
||||
|
||||
func text(_ s: String, size: CGFloat, weight: NSFont.Weight, y: CGFloat, alpha: CGFloat) {
|
||||
let a: [NSAttributedString.Key: Any] = [.font: NSFont.systemFont(ofSize: size, weight: weight),
|
||||
.foregroundColor: NSColor(red: 0.10, green: 0.15, blue: 0.30, alpha: alpha)]
|
||||
let sz = (s as NSString).size(withAttributes: a)
|
||||
(s as NSString).draw(at: NSPoint(x: (w - sz.width) / 2, y: h - y - sz.height), withAttributes: a)
|
||||
}
|
||||
text("Drag Second Display to Applications", size: 20, weight: .semibold, y: 28, alpha: 1)
|
||||
text("Blocked on first launch? Open “Read Me First.txt”", size: 13, weight: .regular, y: 330, alpha: 0.65)
|
||||
|
||||
// arrow between the two icons (icon centres at x=198 and x=529, y=195 from top)
|
||||
let ay = h - 195
|
||||
let arrow = NSBezierPath()
|
||||
arrow.lineWidth = 6; arrow.lineCapStyle = .round; arrow.lineJoinStyle = .round
|
||||
arrow.move(to: NSPoint(x: 290, y: ay)); arrow.line(to: NSPoint(x: 430, y: ay))
|
||||
arrow.move(to: NSPoint(x: 405, y: ay + 22)); arrow.line(to: NSPoint(x: 432, y: ay)); arrow.line(to: NSPoint(x: 405, y: ay - 22))
|
||||
NSColor(red: 0.25, green: 0.35, blue: 0.95, alpha: 0.8).setStroke()
|
||||
arrow.stroke()
|
||||
|
||||
NSGraphicsContext.restoreGraphicsState()
|
||||
try! rep.representation(using: .png, properties: [:])!.write(to: URL(fileURLWithPath: "Config/dmg/background.png"))
|
||||
Reference in new issue
Block a user