tamasgal
tamasgal

Reputation: 26259

Programmatically put a Mac into sleep

I can't find any instructions how to put a Mac programmatically into sleep mode (in Objective-C). I'm sure it should be only one line, but could you give me a hint?

Upvotes: 8

Views: 4567

Answers (6)

prazuber
prazuber

Reputation: 1362

Just in case someone is curious how pmset sleepnow actually works - it uses IOPMSleepSystem API from the Power Management section of the IOKit framework. You can check this via examining the pmset.c source code (link from macOS 10.13.3).

So instead of calling pmset you can request sleep via the following snippet:

#include <IOKit/pwr_mgt/IOPMLib.h>

void SleepNow()
{
    io_connect_t fb = IOPMFindPowerManagement(MACH_PORT_NULL);
    if (fb != MACH_PORT_NULL)
    {
        IOPMSleepSystem(fb);
        IOServiceClose(fb);
    }
}

Don't be scared by the caller must be root or the console user remark in the documentation since it appears to be working for any standard logged in user.

By following the source code, it looks like it calls into IOUserClient::clientHasPrivilege with kIOClientPrivilegeLocalUser which ends up checking if the caller is present in the IOConsoleUsers array in the root IORegistry entry, and apparently currently logged in user is always present there.

Upvotes: 3

Chanok
Chanok

Reputation: 800

#include <stdio.h> 
#include <CoreServices/CoreServices.h>
#include <Carbon/Carbon.h>

SendAppleEventToSystemProcess(kAESleep);

OSStatus SendAppleEventToSystemProcess(AEEventID EventToSend)
{
    AEAddressDesc targetDesc;
    static const ProcessSerialNumber kPSNOfSystemProcess = { 0, kSystemProcess };
    AppleEvent eventReply = {typeNull, NULL};
    AppleEvent appleEventToSend = {typeNull, NULL};

    OSStatus error = noErr;

    error = AECreateDesc(typeProcessSerialNumber, &kPSNOfSystemProcess, 
                                            sizeof(kPSNOfSystemProcess), &targetDesc);

    if (error != noErr)
    {
        return(error);
    }

    error = AECreateAppleEvent(kCoreEventClass, EventToSend, &targetDesc, 
                   kAutoGenerateReturnID, kAnyTransactionID, &appleEventToSend);

    AEDisposeDesc(&targetDesc);
    if (error != noErr)
    {
        return(error);
    }

    error = AESend(&appleEventToSend, &eventReply, kAENoReply, 
                  kAENormalPriority, kAEDefaultTimeout, NULL, NULL);

    AEDisposeDesc(&appleEventToSend);
    if (error != noErr)
    {
        return(error);
    }

    AEDisposeDesc(&eventReply);

    return(error); 
}

More detail on https://developer.apple.com/library/content/qa/qa1134/_index.html

Upvotes: 10

December
December

Reputation: 584

Tom is correct. The AE methods fail if the display is sleeping. pmset sleepnow works 100%.

NSTask  *pmsetTask = [[NSTask alloc] init];
pmsetTask.launchPath = @"/usr/bin/pmset";
pmsetTask.arguments = @[@"sleepnow"];
[pmsetTask launch];

Upvotes: 7

Tom Ritter
Tom Ritter

Reputation: 101330

I found that running pmset sleepnow worked during a screensaver, while the first two answers did not.

Upvotes: 1

xXx
xXx

Reputation: 81

You can also use scripting bridge. Draft code is

SystemEventsApplication *systemEvents = [SBApplication applicationWithBundleIdentifier:@"com.apple.systemevents"];
[systemEvents sleep]; 

Upvotes: 8

cem
cem

Reputation: 3321

You can use AppleScript

NSAppleScript *script = [[NSAppleScript alloc] initWithSource:@"tell application \"System Events\" to sleep"];
NSDictionary *errorInfo;
[script executeAndReturnError:&errorInfo];
[script release];

Upvotes: 6

Related Questions