How to programmatically prevent a Mac from going to sleep?
Asked Answered
V

3

37

Is there way to prevent a Mac from going to sleep programmatically using Objective-C? The I/O kit fundamentals section on Apple's dev site tells me that a driver gets notified of an idle / system sleep, but I can't find a way of preventing the system from sleeping. Is it even possible?

I've come across some other solutions using Caffeine, jiggler, sleepless and even AppleScript, but I want to do this in Objective-C. Thanks.

Valse answered 8/4, 2011 at 14:10 Comment(0)
L
36

Here is the official Apple documentation (including code snippet):
Technical Q&A QA1340 - How to I prevent sleep?

Quote: Preventing sleep using I/O Kit in Mac OS X 10.6 Snow Leopard:

#import <IOKit/pwr_mgt/IOPMLib.h>

// kIOPMAssertionTypeNoDisplaySleep prevents display sleep,
// kIOPMAssertionTypeNoIdleSleep prevents idle sleep

// reasonForActivity is a descriptive string used by the system whenever it needs 
// to tell the user why the system is not sleeping. For example, 
// "Mail Compacting Mailboxes" would be a useful string.

// NOTE: IOPMAssertionCreateWithName limits the string to 128 characters. 
CFStringRef* reasonForActivity= CFSTR("Describe Activity Type");

IOPMAssertionID assertionID;
IOReturn success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, 
                                    kIOPMAssertionLevelOn, reasonForActivity, &assertionID); 
if (success == kIOReturnSuccess)
{
    //  Add the work you need to do without 
    //  the system sleeping here.

    success = IOPMAssertionRelease(assertionID);
    //  The system will be able to sleep again. 
}

For older OSX version, check the following:
Technical Q&A QA1160 - How can I prevent system sleep while my application is running?

Quote: Example usage of UpdateSystemActivity (the canonical way for < 10.6)

#include <CoreServices/CoreServices.h>

void
MyTimerCallback(CFRunLoopTimerRef timer, void *info)
{
    UpdateSystemActivity(OverallAct);
}


int
main (int argc, const char * argv[])
{
    CFRunLoopTimerRef timer;
    CFRunLoopTimerContext context = { 0, NULL, NULL, NULL, NULL };

    timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), 30, 0, 0, MyTimerCallback, &context);
    if (timer != NULL) {
        CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes);
    }

    /* Start the run loop to receive timer callbacks. You don't need to
    call this if you already have a Carbon or Cocoa EventLoop running. */
    CFRunLoopRun();

    CFRunLoopTimerInvalidate(timer);
    CFRelease(timer);

    return (0);
}
Lashing answered 8/4, 2011 at 14:58 Comment(3)
I don't think this work for instance when the macbook lid is closed... How would you prevent sleep then?Doctrinaire
@DavidKarlsson There are two types of sleep; idle and forced. Idle can be controlled by your app, whereas forced cannot. Closing the MacBook lid forces sleep.Cullum
This answer works to "prevent" sleep if the monitor is currently lit. For the question of how to wake up an already sleeping display see #10599309 For the question of "lid closed" see #3316185Isolde
M
12

Apple's Q&A1340 replaces Q&A1160. The latest Q&A answers the question "Q: How can my application get notified when the computer is going to sleep or waking from sleep? How do I prevent sleep?"

Listing 2 of Q&A1340:

#import <IOKit/pwr_mgt/IOPMLib.h>

// kIOPMAssertionTypeNoDisplaySleep prevents display sleep,
// kIOPMAssertionTypeNoIdleSleep prevents idle sleep

//reasonForActivity is a descriptive string used by the system whenever it needs 
//  to tell the user why the system is not sleeping. For example, 
//  "Mail Compacting Mailboxes" would be a useful string.

//  NOTE: IOPMAssertionCreateWithName limits the string to 128 characters. 
CFStringRef* reasonForActivity= CFSTR("Describe Activity Type");

IOPMAssertionID assertionID;
IOReturn success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, 
                                    kIOPMAssertionLevelOn, reasonForActivity, &assertionID); 
if (success == kIOReturnSuccess)
{

    //Add the work you need to do without 
    //  the system sleeping here.

    success = IOPMAssertionRelease(assertionID);
    //The system will be able to sleep again. 
}

Note that you can only stop idle time sleep, not sleep triggered by the user.

For applications supporting Mac OS X 10.6 and later, use the new IOPMAssertion family of functions. These functions allow other applications and utilities to see your application's desire not to sleep; this is critical to working seamlessly with third party power management software.

Maintenance answered 11/12, 2011 at 1:6 Comment(5)
On the CFStringRef* assignment, XCode gives me "incompatible pointer types". I had to add (CFStringRef *) before the CFSTR() call to fix it. Also, you might want to mention that one needs to add the IOKit.framework to their project. Am I correct on both of these?Munn
Also, in the IOPMAssertionCreateWithName() call, I had to add an asterisk *reasonForActivity in order to get it to compile.Munn
@Munn if you feel the code is wrong, please can you report a bug directly to Apple as the code listed is from Q&A1340. Once reported, feel free to add the bug number here to allow others to duplicate or reference the bug in their own reports to Apple.Maintenance
@Munn maybe the corrections you made have the code get compiled, but the IOReturn won't be 0. Instead try to remove the asterisk in the var definition: CFStringRef * reasonForActivity -> CFStringRef reasonForActivityOverslaugh
@Volomike: yes, it's a bug in the Apple example. The asterisk should not be there. It must have been CFStringRef reasonForActivity= CFSTR("Describe Activity Type");. I'm surprised that no one cared to adjust it all this time.Annorah
K
2

Just create an NSTimer that fires a function with this

UpdateSystemActivity(OverallAct);

I'm pretty sure that that's exactly what Caffeine does.

Kmeson answered 8/4, 2011 at 14:31 Comment(4)
Please avoid this trick. Instead use the Apple endorsed techniques documented in Q&A1340.Maintenance
I think he has a point. The “amazing” technique described by Apple is a very poor and crappy solution because you have to embed your code on that thing, making it complex. Now imagine if the code is asynchronous. Also, Apple could not even bother to write the code without errors. Zero stars for Apple.Tristatristam
This is deprecated in OSX 10.8.Munn
@SpaceDog actually is not so complex. You don't have to put your code into the if clause as stated in the sample code comments. Keep the assertionID in a variable and run the IOPMAssertionRelease(assertionID) function later instead, whenever you'd want to reactivate de idle functionality, or never at all.Overslaugh

© 2022 - 2024 — McMap. All rights reserved.