import Cocoa // Curtain — a privacy curtain for macOS Screen Sharing. // Normally launches as a background menu-bar agent (no Dock icon). // // Hidden build helper: `Curtain --render-icon ` writes an .iconset of PNGs so // install.sh can build AppIcon.icns without shipping any image assets. if CommandLine.arguments.contains("--render-icon"), let dirIndex = CommandLine.arguments.firstIndex(of: "--render-icon"), CommandLine.arguments.indices.contains(dirIndex + 1) { CurtainIcon.exportIconset(to: CommandLine.arguments[dirIndex + 1]) // offscreen bitmap render exit(0) } // The AppKit bootstrap is main-actor work; top-level code is nonisolated under Swift 6, // so run it inside an assumeIsolated block (process start is already on the main thread). // app.run() blocks here and never returns, so `delegate` and the signal source stay // retained on this stack. NSApplication.delegate is weak, so `delegate` must be held. MainActor.assumeIsolated { let app = NSApplication.shared let delegate = AppDelegate() app.delegate = delegate app.setActivationPolicy(.accessory) // background agent; settings window still shows // SIGTERM (launchd stop / `kill`): a C signal handler can't safely touch AppKit, so // ignore the default action and route the signal through a DispatchSource on the main // queue, where it can run cleanup before exiting. signal(SIGTERM, SIG_IGN) let sigtermSource = DispatchSource.makeSignalSource(signal: SIGTERM, queue: .main) sigtermSource.setEventHandler { MainActor.assumeIsolated { delegate.cleanup() } exit(0) } sigtermSource.resume() app.run() }