Name the app bundle 'Second Display' everywhere; second-display for file artifacts

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
mmacierzankaandClaude Bot committed 2026-11-03 14:01:46 +01:00
1 parent 8072ac473a
commit d3609ae29c
8 files changed
+40 -35

No files matched your search

+2 -2
View File
@@ -15,10 +15,10 @@ A native macOS app that shows video from a capture device (camera / capture card
## Structure
- `Sources/` – app code (AppKit + AVFoundation, one `AVCaptureSession`, audio via `AVCaptureAudioPreviewOutput`). The `App` class is split into extensions:
`main.swift` (entry point), `App.swift` (state, window, lifecycle), `Capture.swift` (devices, formats, hot-plug), `Menu.swift` (menus and actions), `Power.swift` (sleep prevention), `Autostart.swift` (launch-on-connect agent), `Preferences.swift` (UserDefaults-backed settings).
- `Config/Info.plist`, `Config/SecondDisplay.entitlements`, `Config/dmg/` – version, permissions (camera, microphone; hardened runtime).
- `Config/Info.plist`, `Config/second-display.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/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 (`-`).
- `Makefile` – `make` (builds `build/Second Display.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
- Settings live in `UserDefaults` (keys: `video`, `audio`, `autostart`, `autostartName`, `keepAwake`, `startFull`, `onTop`, `gravity`, `format.<id>`).
+1 -1
View File
@@ -4,7 +4,7 @@
<dict>
<key>CFBundleDevelopmentRegion</key><string>en</string>
<key>CFBundleLocalizations</key><array><string>en</string><string>pl</string></array>
<key>CFBundleExecutable</key><string>SecondDisplay</string>
<key>CFBundleExecutable</key><string>Second Display</string>
<key>CFBundleIdentifier</key><string>com.mmacierzanka.seconddisplay</string>
<key>CFBundleName</key><string>Second Display</string>
<key>CFBundlePackageType</key><string>APPL</string>
+4 -4
View File
@@ -1,7 +1,7 @@
SECOND DISPLAY — INSTALLATION
=============================
1. Drag "SecondDisplay" onto the "Applications" folder in this window.
1. Drag "Second Display" onto the "Applications" folder in this window.
2. Open Second Display from Applications.
macOS will most likely refuse: this app is not notarized by Apple
@@ -13,11 +13,11 @@ SECOND DISPLAY — INSTALLATION
A) System Settings
- Open System Settings > Privacy & Security.
- Scroll to the Security section; you will see
"SecondDisplay was blocked...". Click "Open Anyway".
"Second Display was blocked...". Click "Open Anyway".
- Confirm with your password / Touch ID, then click "Open".
B) Terminal
xattr -dr com.apple.quarantine /Applications/SecondDisplay.app
xattr -dr com.apple.quarantine "/Applications/Second Display.app"
You only need to do this once per downloaded copy.
@@ -29,4 +29,4 @@ REQUIREMENTS
UNINSTALL
Run this in Terminal to remove the app and every trace it leaves:
/Applications/SecondDisplay.app/Contents/Resources/uninstall.sh
"/Applications/Second Display.app/Contents/Resources/uninstall.sh"
+1 -1
View File
@@ -17,7 +17,7 @@ icon_size = 128
text_size = 13
arrange_by = None
icon_locations = {
'SecondDisplay.app': (198, 195),
'Second Display.app': (198, 195),
'Applications': (529, 195),
'Read Me First.txt': (363, 435),
}
File renamed without changes.
+26 -21
View File
@@ -1,47 +1,52 @@
APP := build/SecondDisplay.app
NAME := Second Display
APP := build/$(NAME).app
# GNU make cannot use paths with spaces as targets, so builds are tracked by a stamp file
STAMP := build/.app-built
SRC := $(wildcard Sources/*.swift)
RES := $(shell find Resources -type f)
ENTITLEMENTS := Config/second-display.entitlements
# "-" = ad-hoc; set SIGN_ID to a Developer ID identity for distribution
SIGN_ID ?= -
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
DMG := build/second-display-$(VERSION).dmg
VOLNAME ?= $(NAME)
.PHONY: build run clean install uninstall check dmg help
build: $(APP) ## Build build/SecondDisplay.app
build: $(STAMP) ## Build build/Second Display.app
$(APP): Makefile $(SRC) $(RES) Config/Info.plist Config/SecondDisplay.entitlements
rm -rf $(APP) && mkdir -p $(APP)/Contents/MacOS $(APP)/Contents/Resources
swiftc -O -target arm64-apple-macos26.0 $(SRC) -o $(APP)/Contents/MacOS/SecondDisplay-arm64
swiftc -O -target x86_64-apple-macos26.0 $(SRC) -o $(APP)/Contents/MacOS/SecondDisplay-x86_64
lipo -create $(APP)/Contents/MacOS/SecondDisplay-* -output $(APP)/Contents/MacOS/SecondDisplay
rm $(APP)/Contents/MacOS/SecondDisplay-*
cp Config/Info.plist $(APP)/Contents/Info.plist
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $(BUILDNUM)" $(APP)/Contents/Info.plist
cp -R Resources/en.lproj Resources/pl.lproj Resources/uninstall.sh $(APP)/Contents/Resources/
xcrun actool Resources/AppIcon.icon --compile $(APP)/Contents/Resources --platform macosx \
$(STAMP): Makefile $(SRC) $(RES) Config/Info.plist $(ENTITLEMENTS)
rm -rf "$(APP)" && mkdir -p "$(APP)/Contents/MacOS" "$(APP)/Contents/Resources"
swiftc -O -target arm64-apple-macos26.0 $(SRC) -o build/arm64
swiftc -O -target x86_64-apple-macos26.0 $(SRC) -o build/x86_64
lipo -create build/arm64 build/x86_64 -output "$(APP)/Contents/MacOS/$(NAME)"
rm build/arm64 build/x86_64
cp Config/Info.plist "$(APP)/Contents/Info.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $(BUILDNUM)" "$(APP)/Contents/Info.plist"
cp -R Resources/en.lproj Resources/pl.lproj Resources/uninstall.sh "$(APP)/Contents/Resources/"
xcrun actool Resources/AppIcon.icon --compile "$(APP)/Contents/Resources" --platform macosx \
--minimum-deployment-target 26.0 --app-icon AppIcon --output-partial-info-plist /dev/null >/dev/null
codesign --force --options runtime --entitlements Config/SecondDisplay.entitlements --sign "$(SIGN_ID)" $(APP)
codesign --force --options runtime --entitlements $(ENTITLEMENTS) --sign "$(SIGN_ID)" "$(APP)"
touch $(STAMP)
run: build ## Build and launch
open $(APP)
open "$(APP)"
clean: ## Remove build output
rm -rf build
install: build ## Copy to ~/Applications
mkdir -p ~/Applications && rm -rf ~/Applications/SecondDisplay.app && cp -R $(APP) ~/Applications/
mkdir -p ~/Applications && rm -rf ~/Applications/SecondDisplay.app "$$HOME/Applications/$(NAME).app" && cp -R "$(APP)" ~/Applications/
uninstall: ## Remove the installed app and all its leftovers
~/Applications/SecondDisplay.app/Contents/Resources/uninstall.sh
"$$HOME/Applications/$(NAME).app/Contents/Resources/uninstall.sh"
check: build ## Lint sources, plists, strings and the built bundle
swiftc -typecheck $(SRC)
plutil -lint Config/Info.plist Config/SecondDisplay.entitlements Resources/*.lproj/*.strings
plutil -lint Config/Info.plist $(ENTITLEMENTS) Resources/*.lproj/*.strings
scripts/check-strings.sh
codesign --verify --strict $(APP)
codesign --verify --strict "$(APP)"
DMGBUILD := build/venv/bin/dmgbuild
@@ -50,7 +55,7 @@ $(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)
$(DMGBUILD) -s Config/dmg/settings.py -D app="$(APP)" "$(VOLNAME)" $(DMG)
hdiutil verify $(DMG) | tail -1
help: ## List targets
+5 -5
View File
@@ -7,11 +7,11 @@ macOS 26+, Xcode (Command Line Tools).
## Build and run
```
make # builds build/SecondDisplay.app
make # builds build/Second Display.app
make run # build and launch
make check # lint: typecheck, plists, translations, signature
make install # copy to ~/Applications
make dmg # build/SecondDisplay-<version>.dmg (universal, not notarized)
make dmg # build/second-display-<version>.dmg (universal, not notarized)
make help # list targets
```
The app is signed ad-hoc (works on this Mac). For distribution: `make SIGN_ID="Developer ID Application: ..."` (notarization is still required).
@@ -22,11 +22,11 @@ Build from source and install to `~/Applications`:
git clone <repository-url> second-display
cd second-display
make install
open ~/Applications/SecondDisplay.app
open "$HOME/Applications/Second Display.app"
```
On first launch macOS asks for camera and microphone access — allow both. You can change this later in System Settings → Privacy & Security.
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.
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/Second Display.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/`) 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`).
@@ -44,7 +44,7 @@ Menus at the top of the screen: **Video** (device, format), **Audio** (source or
## Settings and uninstall
Settings: `~/Library/Preferences/com.mmacierzanka.seconddisplay.plist`.
To remove the app and all its traces: `make uninstall` or `SecondDisplay.app/Contents/Resources/uninstall.sh`.
To remove the app and all its traces: `make uninstall` or `"Second Display.app/Contents/Resources/uninstall.sh"`.
## License
MIT — see [LICENSE](LICENSE).
+1 -1
View File
@@ -5,7 +5,7 @@ APP="${0:A:h:h:h}"
[[ "$APP" == *.app && "$(defaults read "$APP/Contents/Info" CFBundleIdentifier 2>/dev/null)" == "$ID" ]] || { echo "Not a Second Display bundle: $APP"; exit 1; }
read -q "?Delete $APP and all its data? [y/N] " || { echo; echo Cancelled; exit 0; }
echo
pkill -x SecondDisplay 2>/dev/null
pkill -x "Second Display" 2>/dev/null; pkill -x SecondDisplay 2>/dev/null # current and legacy executable names
launchctl bootout "gui/$(id -u)/$ID.autostart" 2>/dev/null # launch-on-connect agent
rm -f ~/Library/LaunchAgents/$ID.autostart.plist
defaults delete "$ID" 2>/dev/null