8

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?

jscs
  • 63,694
  • 13
  • 151
  • 195
tamasgal
  • 24,826
  • 18
  • 96
  • 135
  • Related: http://stackoverflow.com/questions/3315685/how-to-wake-from-sleep-programmatically –  Jun 05 '11 at 13:38
  • Yea it's kind of related, but not answering my question anyhow. – tamasgal Jun 05 '11 at 13:43
  • But it might be interesting for others who visit this question. –  Jun 05 '11 at 13:44
  • 2
    You [can call `Sleep`](http://stackoverflow.com/questions/3827158/what-does-a-c-function-named-sleep-do-on-a-mac), but I hope there’s actually a proper Cocoa function. – Josh Lee Jun 05 '11 at 13:54
  • 3
    jleedev, you misunderstood my question. I asked for putting the Mac into sleep mode ;-) – tamasgal Jun 05 '11 at 15:15
  • @septi: Actually, no, he did not misunderstand the question. `Sleep` is an old function that puts the machine to sleep. Nowadays, it's not even declared in the headers, but it does still exist even in 64-bit and commenters on the question @jleedev linked to reported that it still works. – Peter Hosey Jun 06 '11 at 01:52
  • (Remember that C, and, by extension, Objective-C, is case-sensitive. `Sleep` is not the same as `sleep`; they are not the same function, and, as anyone who calls the wrong one will find out, they do not do the same thing.) – Peter Hosey Jun 06 '11 at 01:52

6 Answers6

9
#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

mmsarquis
  • 170
  • 3
  • 16
Chanok
  • 790
  • 6
  • 23
  • Here's a version that doesn't require importing anything and works on 64-bit: http://stackoverflow.com/a/6283690/35690 – Senseful Apr 27 '14 at 22:26
8

You can also use scripting bridge. Draft code is

SystemEventsApplication *systemEvents = [SBApplication applicationWithBundleIdentifier:@"com.apple.systemevents"];
[systemEvents sleep]; 
xXx
  • 81
  • 4
  • This one works in a sandboxed app on Mountain Lion, too. Thanks! :) – Form Aug 01 '12 at 02:06
  • sorry for the late reply, but this one doesn't work in a sandboxed app anymore. Didn't find another way either. – Gary Oct 02 '15 at 09:48
7

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];
December
  • 584
  • 5
  • 10
6

You can use AppleScript

NSAppleScript *script = [[NSAppleScript alloc] initWithSource:@"tell application \"System Events\" to sleep"];
NSDictionary *errorInfo;
[script executeAndReturnError:&errorInfo];
[script release];
cem
  • 3,311
  • 1
  • 18
  • 23
2

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.

prazuber
  • 1,352
  • 10
  • 26
1

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

Tom Ritter
  • 99,986
  • 30
  • 138
  • 174