Created
April 7, 2016 08:15
-
-
Save pudquick/9683c333e73a82379b8e377eb2e6fc41 to your computer and use it in GitHub Desktop.
Send polite Logout / "really" Logout / Restart / Shutdown Apple Events to loginwindow via python and pyObjC
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
import struct, objc | |
from Foundation import NSBundle | |
from Cocoa import NSAppleEventDescriptor | |
def OSType(s): | |
# Convert 4 character code into 4 byte integer | |
return struct.unpack('>I', s)[0] | |
# Create an opaque pointer type to mask the raw AEDesc pointers we'll throw around | |
AEDescRef = objc.createOpaquePointerType('AEDescRef', '^{AEDesc=I^^{OpaqueAEDataStorageType}}') | |
# Load AESendMessage from AE.framework for sending the AppleEvent | |
AE_bundle = NSBundle.bundleWithIdentifier_('com.apple.AE') | |
functions = [("AESendMessage", b"i^{AEDesc=I^^{OpaqueAEDataStorageType}}^{AEDesc=I^^{OpaqueAEDataStorageType}}iq"),] | |
objc.loadBundleFunctions(AE_bundle, globals(), functions) | |
# defined in AEDataModel.h | |
kAENoReply = 1 | |
kAENeverInteract = 16 | |
kAEDefaultTimeout = -1 | |
kAnyTransactionID = 0 | |
kAutoGenerateReturnID = -1 | |
# defined in AEDataModel.h | |
typeAppleEvent = OSType('aevt') | |
typeApplicationBundleID = OSType('bund') | |
# defined in AERegistry.h | |
kAELogOut = OSType('logo') | |
kAEReallyLogOut = OSType('rlgo') | |
kAEShowRestartDialog = OSType('rrst') | |
kAEShowShutdownDialog = OSType('rsdn') | |
# Build a standalone application descriptor by bundle id | |
loginwindowDesc = NSAppleEventDescriptor.alloc().initWithDescriptorType_data_(typeApplicationBundleID, buffer('com.apple.loginwindow')) | |
# build an event descriptor with our app descriptor as the target and the kAELogOut eventID | |
event = NSAppleEventDescriptor.appleEventWithEventClass_eventID_targetDescriptor_returnID_transactionID_( | |
typeAppleEvent, kAELogOut, loginwindowDesc, kAutoGenerateReturnID, kAnyTransactionID) | |
eventDesc = event.aeDesc() | |
# Send a polite logout (returns immediately) | |
err = AESendMessage(eventDesc, None, kAENoReply|kAENeverInteract, kAEDefaultTimeout) |
Round 3, via NSAppleScript:
from Foundation import NSAppleScript
script = u"""ignoring application responses
tell application "loginwindow"
\u300Aevent aevtlogo\u300B
end tell
end ignoring
"""
logout = NSAppleScript.alloc().initWithSource_(script)
result = logout.executeAndReturnError_(None)
Same issues as the SBApplication method, in terms of effectiveness/usability. Had to use the "asian encoding compatible" chevrons listed here: https://developer.apple.com/library/mac/documentation/AppleScript/Conceptual/AppleScriptLangGuide/conceptual/ASLR_lexical_conventions.html#//apple_ref/doc/uid/TP40000983-CH214-SW5
Likely because the original left/right chevrons were part of MacRoman encoding and NSString <-> NSAppleScript communication trips all over itself about how to interpret. Even if you build the NSString from data with the right encoding specified, NSAppleScript keeps receiving the UTF-8 encoded form. By moving to the alternate encoding, we're forcing pure Unicode.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
And much less effective version, via SBApplication scripting bridge:
Less effective because it triggers a bundle/GUI application conversion (triggering the Dock rocketship icon) plus doesn't work from root/anything other than the logged in user context