Replace build.sh with Makefile; add Info.plist, entitlements, hardened runtime, make check

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
mmacierzankaandClaude Bot committed 2026-11-03 02:36:16 +01:00
1 parent 3885a04560
commit 00fa7f9546
8 files changed
+116 -32

No files matched your search

+1 -1
View File
@@ -1,2 +1,2 @@
SecondDisplay.app/
build/
.DS_Store
+5 -4
View File
@@ -13,12 +13,13 @@ Aplikacja natywna na macOS, która wyświetla w oknie obraz z urządzenia przech
## Struktura
- `Sources/main.swift` – cała aplikacja (AppKit + AVFoundation, jedna sesja `AVCaptureSession`, dźwięk przez `AVCaptureAudioPreviewOutput`).
- `Resources/{en,pl}.lproj` – `Localizable.strings` i `InfoPlist.strings`.
- `Resources/AppIcon.icon` – ikona w formacie Icon Composer (Liquid Glass; otwórz w Icon Composer, warstwy SVG w `Assets/`); `build.sh` kompiluje ją przez `actool`.
- `Resources/uninstall.sh` – kopiowany do bundla (`Contents/Resources/uninstall.sh`); usuwa aplikację, preferencje, cache, uprawnienia TCC. Bundle ID: `com.mmacierzanka.seconddisplay`.
- `build.sh` – buduje i podpisuje (ad-hoc) `SecondDisplay.app`; uruchom `./build.sh`, potem `open SecondDisplay.app`.
- `Config/Info.plist`, `Config/SecondDisplay.entitlements` – wersja, uprawnienia (kamera, mikrofon; hardened runtime).
- `Resources/` – `AppIcon.icon` (Icon Composer, Liquid Glass; kompilowana przez `actool`), `{en,pl}.lproj` (`Localizable.strings`, `InfoPlist.strings`), `uninstall.sh` (kopiowany do bundla; usuwa aplikację, preferencje, cache, uprawnienia TCC). Bundle ID: `com.mmacierzanka.seconddisplay`.
- `scripts/check-strings.sh` – spójność tłumaczeń en/pl.
- `Makefile` – `make` (build do `build/SecondDisplay.app`), `run`, `check`, `install`, `uninstall`, `clean`, `help`. `SIGN_ID` domyślnie ad-hoc (`-`).
## Konwencje
- Ustawienia w `UserDefaults` (klucze: `video`, `audio`, `keepAwake`, `startFull`, `onTop`, `gravity`, `format.<id>`).
- Nowe napisy dodawaj przez `NSLocalizedString` oraz do obu plików `Localizable.strings`.
- Przed commitem: `make check`. Wersja w `Config/Info.plist`, numer buildu = liczba commitów.
- Commity: małe, osobne na funkcję, z linią `Co-Authored-By`.
+22
View File
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<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>CFBundleIdentifier</key><string>com.mmacierzanka.seconddisplay</string>
<key>CFBundleName</key><string>Second Display</string>
<key>CFBundlePackageType</key><string>APPL</string>
<key>CFBundleShortVersionString</key><string>0.1.0</string>
<key>CFBundleVersion</key><string>1</string>
<key>CFBundleIconFile</key><string>AppIcon</string>
<key>CFBundleIconName</key><string>AppIcon</string>
<key>LSMinimumSystemVersion</key><string>26.0</string>
<key>LSApplicationCategoryType</key><string>public.app-category.video</string>
<key>NSHumanReadableCopyright</key><string>Copyright © 2026 mmacierzanka</string>
<key>NSPrincipalClass</key><string>NSApplication</string>
<key>NSCameraUsageDescription</key><string>Shows video from the capture device.</string>
<key>NSMicrophoneUsageDescription</key><string>Plays audio from the selected input.</string>
</dict>
</plist>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.device.camera</key><true/>
<key>com.apple.security.device.audio-input</key><true/>
</dict>
</plist>
+40
View File
@@ -0,0 +1,40 @@
APP := build/SecondDisplay.app
SRC := $(wildcard Sources/*.swift)
RES := $(shell find Resources -type f)
# "-" = 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)
.PHONY: build run clean install uninstall check help
build: $(APP) ## Build build/SecondDisplay.app
$(APP): Makefile $(SRC) $(RES) Config/Info.plist Config/SecondDisplay.entitlements
rm -rf $(APP) && mkdir -p $(APP)/Contents/MacOS $(APP)/Contents/Resources
swiftc -O $(SRC) -o $(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 \
--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)
run: build ## Build and launch
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/
uninstall: ## Remove the installed app and all its leftovers
~/Applications/SecondDisplay.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
scripts/check-strings.sh
codesign --verify --strict $(APP)
help: ## List targets
@grep -E '^[a-z]+:.*##' Makefile | sed 's/:.*##/ -/'
+29
View File
@@ -0,0 +1,29 @@
# Second Display
Natywna aplikacja macOS, która pokazuje w oknie obraz z urządzenia przechwytującego (capture card, kamerka) i odtwarza dźwięk z wybranego wejścia — jako "drugi monitor".
## Wymagania
macOS 26+, Xcode (Command Line Tools).
## Budowanie i uruchamianie
```
make # buduje build/SecondDisplay.app
make run # buduje i uruchamia
make check # lint: typecheck, plisty, tłumaczenia, podpis
make install # kopiuje do ~/Applications
make help # lista poleceń
```
Podpis jest ad-hoc (działa na tym Macu). Do dystrybucji: `make SIGN_ID="Developer ID Application: ..."` (potrzebna jeszcze notaryzacja).
## Użycie
Menu u góry ekranu: **Obraz** (kamera, format), **Dźwięk** (źródło lub wyłączony), **Opcje** (blokada usypiania, pełny ekran przy starcie, na wierzchu, wyciszenie ⇧⌘M, skalowanie), **Okno** (kafelkowanie, pełny ekran ⌃⌘F).
## Struktura
- `Sources/main.swift` – aplikacja (AppKit + AVFoundation)
- `Config/` – `Info.plist`, entitlements (kamera, mikrofon)
- `Resources/` – ikona (`AppIcon.icon`, Icon Composer), tłumaczenia en/pl, `uninstall.sh`
- `scripts/check-strings.sh` – spójność tłumaczeń
## Ustawienia i odinstalowanie
Ustawienia: `~/Library/Preferences/com.mmacierzanka.seconddisplay.plist`.
Pełne usunięcie aplikacji i śladów: `make uninstall` albo `SecondDisplay.app/Contents/Resources/uninstall.sh`.
-27
View File
@@ -1,27 +0,0 @@
#!/bin/zsh
set -e
cd "$(dirname "$0")"
A="SecondDisplay.app"
rm -rf "$A"; mkdir -p "$A/Contents/MacOS"
swiftc -O Sources/main.swift -o "$A/Contents/MacOS/SecondDisplay"
cat > "$A/Contents/Info.plist" <<P
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><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>CFBundleIdentifier</key><string>com.mmacierzanka.seconddisplay</string>
<key>CFBundleIconFile</key><string>AppIcon</string>
<key>CFBundleIconName</key><string>AppIcon</string>
<key>CFBundleName</key><string>Second Display</string>
<key>CFBundlePackageType</key><string>APPL</string>
<key>NSPrincipalClass</key><string>NSApplication</string>
<key>NSCameraUsageDescription</key><string>Shows video from the capture device.</string>
<key>NSMicrophoneUsageDescription</key><string>Plays audio from the selected input.</string>
</dict></plist>
P
for L in en pl; do mkdir -p "$A/Contents/Resources/$L.lproj"; cp Resources/$L.lproj/*.strings "$A/Contents/Resources/$L.lproj/"; done
xcrun actool Resources/AppIcon.icon --compile "$A/Contents/Resources" --platform macosx --minimum-deployment-target 26.0 --app-icon AppIcon --output-partial-info-plist /dev/null >/dev/null
cp Resources/uninstall.sh "$A/Contents/Resources/"
codesign --force --sign - "$A"
+11
View File
@@ -0,0 +1,11 @@
#!/bin/zsh
# Fails if en/pl Localizable.strings differ in keys or code uses a key missing from them.
cd "$(dirname "$0")/.."
keys() { grep -o '^"[^"]*"' "Resources/$1.lproj/Localizable.strings" | sort; }
used=$(grep -oh 'NSLocalizedString("[^"]*"' Sources/*.swift | sed 's/NSLocalizedString(//' | sort -u)
# keys passed through a variable (e.g. the Fit/Fill/Stretch loop) are not caught by the grep above; list them in the pl/en parity check only
fail=0
diff <(keys en) <(keys pl) >/dev/null || { echo "en and pl keys differ:"; diff <(keys en) <(keys pl); fail=1; }
missing=$(comm -23 <(echo "$used") <(keys en))
[[ -z "$missing" ]] || { echo "Used but missing in en:"; echo "$missing"; fail=1; }
exit $fail