Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions Code Awake Tests/AppLogicTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,26 @@ import XCTest
@testable import Code_Awake

final class AppLogicTests: XCTestCase {
func testLockAndDisplaySleepPolicyLocksBeforeSleepingDisplays() {
XCTAssertEqual(
LockAndDisplaySleepPolicy().commands,
[
SystemToolCommand(
executablePath: "/usr/bin/open",
arguments: ["-a", "ScreenSaverEngine"]
),
SystemToolCommand(
executablePath: "/System/Library/CoreServices/Menu Extras/User.menu/Contents/Resources/CGSession",
arguments: ["-suspend"]
),
SystemToolCommand(
executablePath: "/usr/bin/pmset",
arguments: ["displaysleepnow"]
)
]
)
}

func testAwakeAssertionPolicyLetsMacOSLockAndDisplaySleepWhenEnabled() {
let policy = AwakeAssertionPolicy()

Expand Down
22 changes: 22 additions & 0 deletions Code Awake/AppLogic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,28 @@ struct AwakePowerAssertion: Equatable {
let reason: String
}

struct SystemToolCommand: Equatable {
let executablePath: String
let arguments: [String]
}

struct LockAndDisplaySleepPolicy {
let commands = [
SystemToolCommand(
executablePath: "/usr/bin/open",
arguments: ["-a", "ScreenSaverEngine"]
),
SystemToolCommand(
executablePath: "/System/Library/CoreServices/Menu Extras/User.menu/Contents/Resources/CGSession",
arguments: ["-suspend"]
),
SystemToolCommand(
executablePath: "/usr/bin/pmset",
arguments: ["displaysleepnow"]
)
]
}

struct AwakeAssertionPolicy {
private let systemAssertions = [
AwakePowerAssertion(
Expand Down
40 changes: 26 additions & 14 deletions Code Awake/Code_AwakeApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ struct Code_AwakeApp: App {
awakeController: awakeController,
launchAtLoginController: launchAtLoginController,
updateAction: checkForUpdatesManually,
lockScreenAction: lockScreenNow,
lockScreenAction: lockScreenAndSleepDisplaysNow,
donateAction: openDonatePage,
quitAction: {
awakeController.shutdown()
Expand Down Expand Up @@ -98,24 +98,36 @@ struct Code_AwakeApp: App {
NSWorkspace.shared.open(url)
}

private func lockScreenNow() {
launchSystemTool("/usr/bin/open", arguments: ["-a", "ScreenSaverEngine"])
launchSystemTool(
"/System/Library/CoreServices/Menu Extras/User.menu/Contents/Resources/CGSession",
arguments: ["-suspend"]
)
private func lockScreenAndSleepDisplaysNow() {
runSystemTools(LockAndDisplaySleepPolicy().commands[...])
}

private func launchSystemTool(_ path: String, arguments: [String]) {
guard FileManager.default.isExecutableFile(atPath: path) else {
private func runSystemTools(_ commands: ArraySlice<SystemToolCommand>) {
guard let command = commands.first else {
return
}

let remainingCommands = commands.dropFirst()

guard FileManager.default.isExecutableFile(atPath: command.executablePath) else {
runSystemTools(remainingCommands)
return
}

let process = Process()
process.executableURL = URL(fileURLWithPath: path)
process.arguments = arguments
process.executableURL = URL(fileURLWithPath: command.executablePath)
process.arguments = command.arguments
process.terminationHandler = { _ in
DispatchQueue.main.async {
runSystemTools(remainingCommands)
}
}

try? process.run()
do {
try process.run()
} catch {
runSystemTools(remainingCommands)
}
}
}

Expand Down Expand Up @@ -155,7 +167,7 @@ private struct CodeAwakeMenuPanel: View {
toggleAction: {
awakeController.setAllowLockAndSleepEnabled(!awakeController.allowLockAndSleepEnabled)
},
tooltip: "Allow macOS to lock the screen or sleep while Code Awake is active.",
tooltip: "Use the normal macOS lock and display-sleep schedule while Code Awake keeps the Mac running.",
lockAction: lockScreenAction
)

Expand Down Expand Up @@ -399,7 +411,7 @@ private struct MenuLockSleepRow: View {
.clipShape(Circle())
}
.buttonStyle(.plain)
.help("Lock your screen now.")
.help("Lock the screen and turn displays off now.")
.transition(.scale.combined(with: .opacity))
}

Expand Down
2 changes: 1 addition & 1 deletion Code Awake/WelcomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ struct WelcomeView: View {
UseCaseRow(
icon: "lock",
title: "Optional Lock & Sleep",
detail: "Choose between keeping the display/session awake or letting macOS lock and sleep the display while work continues."
detail: "Choose between keeping the display/session awake or following the normal macOS lock and display-sleep schedule while work continues."
)

UseCaseRow(
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ The app uses public Apple power-management APIs and Sparkle for direct-distribut
| Feature | Details |
| --- | --- |
| Keep Mac Awake | Prevents idle system sleep and keeps network client sessions active. |
| Lock & Sleep | Off keeps display sleep and lock away, then dims supported displays to 10% after one minute. On lets macOS lock and display-sleep timing work while the Mac stays awake. |
| Lock & Sleep | Off keeps display sleep and lock away, then dims supported displays to 10% after one minute. On follows the normal macOS lock and display-sleep schedule while the Mac stays awake. Its lock button locks the screen and sleeps displays immediately. |
| Closed Lid Workflows | Best-effort support when macOS allows closed-lid wake behavior, such as supported clamshell conditions. |
| Auto Turn Off | Optional timer for turning awake mode off automatically. |
| Launch at Login | Starts Code Awake with macOS so the menu bar control is ready. |
Expand Down