mirror of
https://github.com/acamarata/curtain.git
synced 2026-07-01 03:04:25 +00:00
Rework Curtain into a proper menu-bar app (Caffeine-style): - SwiftUI settings window: app, on-start, on-idle, on-end, security, displays - Modular ActionSet/ActionRunner — per-event toggles (disconnect/lock/screen-off/deactivate) - Configurable idle timeout; open-at-login via SMAppService; optional menu bar - Settings persisted in UserDefaults (shared with @AppStorage); salted-SHA256 password - Drawn curtains logo (menu-bar template + offscreen-rendered app icon) - Split into single-responsibility modules with comment blocks
46 lines
1.6 KiB
Swift
46 lines
1.6 KiB
Swift
import Foundation
|
|
|
|
/// Purpose: A composable set of lifecycle actions. Each phase (start / idle / end)
|
|
/// maps to an ActionSet; the runner performs only the enabled actions.
|
|
/// Keeping actions independent and data-driven means new behaviors are a
|
|
/// field here, not a branch scattered across the app.
|
|
/// SPORT: MASTER-ACTIONS
|
|
struct ActionSet {
|
|
var activateCurtain = false
|
|
var disconnect = false
|
|
var lock = false
|
|
var screenOff = false
|
|
var deactivateCurtain = false
|
|
}
|
|
|
|
/// Performs an ActionSet against the live curtain + system. Ordering is deliberate:
|
|
/// disconnect the operator first, deactivate/cover the screen, then lock, then sleep
|
|
/// displays last (so the lock is in place before the panels go dark).
|
|
struct ActionRunner {
|
|
let curtain: CurtainController
|
|
let input: InputFilter
|
|
|
|
func run(_ set: ActionSet) {
|
|
if set.activateCurtain { activateCover() }
|
|
if set.disconnect { System.endScreenShareSession() }
|
|
if set.deactivateCurtain { deactivateCover() }
|
|
if set.lock { System.lockScreen() }
|
|
if set.screenOff {
|
|
// Give the lock a beat to take hold before the displays sleep.
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { System.sleepDisplays() }
|
|
}
|
|
}
|
|
|
|
func activateCover() {
|
|
guard !curtain.isShown else { return }
|
|
curtain.show()
|
|
System.preventDisplaySleep()
|
|
_ = input.start() // no-op result: cover still hides even without Accessibility
|
|
}
|
|
|
|
func deactivateCover() {
|
|
input.stop()
|
|
curtain.hide()
|
|
System.allowDisplaySleep()
|
|
}
|
|
}
|