Last active
March 30, 2023 13:40
-
-
Save ABridoux/8efb76bdd4ab070d5ea4a18b33a34144 to your computer and use it in GitHub Desktop.
A service to send events to restart, shutdown, put to sleep or logout the computer.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Free to use | |
// Written by Alexis Bridoux - https://github.com/ABridoux | |
import AppKit | |
import Foundation | |
/// Service to shut down, restart, or put the computer to sleep. Also log out the user. | |
/// | |
/// ### Resources | |
/// - [Apple doc](https://developer.apple.com/library/archive/qa/qa1134/_index.html) | |
/// - Already in use in [SplashBuddy](https://github.com/macadmins/SplashBuddy/blob/main/SplashBuddy/Tools/LoginWindow.swift) | |
enum EventService {} | |
// MARK: - Logic | |
extension EventService { | |
static func send(event eventType: AppleEventType) throws { | |
// target the login window process for the event | |
var loginWindowSerialNumber = ProcessSerialNumber( | |
highLongOfPSN: 0, | |
lowLongOfPSN: UInt32(kSystemProcess) | |
) | |
var targetDesc = AEAddressDesc() | |
var error = OSErr() | |
error = AECreateDesc( | |
keyProcessSerialNumber, | |
&loginWindowSerialNumber, | |
MemoryLayout<ProcessSerialNumber>.size, | |
&targetDesc | |
) | |
if error != noErr { | |
throw EventError( | |
errorDescription: "Unable to create the description of the app. Status: \(error)" | |
) | |
} | |
// create the Apple event | |
var event = AppleEvent() | |
error = AECreateAppleEvent( | |
kCoreEventClass, | |
eventType.eventId, | |
&targetDesc, | |
AEReturnID(kAutoGenerateReturnID), | |
AETransactionID(kAnyTransactionID), | |
&event | |
) | |
AEDisposeDesc(&targetDesc) | |
if error != noErr { | |
throw EventError( | |
errorDescription: "Unable to create an Apple Event for the app description. Status: \(error)" | |
) | |
} | |
// send the event | |
var reply = AppleEvent() | |
let status = AESendMessage( | |
&event, | |
&reply, | |
AESendMode(kAENoReply), | |
1000 | |
) | |
if status != noErr { | |
throw EventError( | |
errorDescription: "Error while sending the event \(eventType). Status: \(status)" | |
) | |
} | |
AEDisposeDesc(&event) | |
AEDisposeDesc(&reply) | |
} | |
} | |
// MARK: - Models | |
extension EventService { | |
enum AppleEventType: String { | |
case shutdownComputer = "Shut down the computer" | |
case restartComputer = "Restart the computer" | |
case asleepComputer = "Asleep the computer" | |
case logoutUser = "Logout the user" | |
var eventId: OSType { | |
switch self { | |
case .shutdownComputer: return kAEShutDown | |
case .restartComputer: return kAERestart | |
case .putComputerToSleep: return kAESleep | |
case .logoutUser: return kAEReallyLogOut | |
} | |
} | |
} | |
} | |
extension EventService.AppleEventType: CaseIterable, Identifiable { | |
var id: String { rawValue } | |
} | |
extension EventService { | |
struct EventError: LocalizedError { | |
var errorDescription: String? | |
} | |
} |
Is this still valid as of macOS 13 ? I tried using it but XCode complains that it can't find 'kSystemProcess' in scope (line 21).
Is this still valid as of macOS 13 ? I tried using it but XCode complains that it can't find 'kSystemProcess' in scope (line 21).
It seems it's not anymore in Foundation
(or I was mistaken). Works fine when you import AppKit
. I am updating the gist.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Explanations
Read the article
How to use it
First it's required to add the following key to the app entitlements:
Then