14
Oct
2009
 

The journey to disabling sleep with IOKit

by Fraser Hess

If your app is fullscreen, like a game, has a presentation mode, or plays long running movie files, you’ll want to disable the display from sleeping. DVD Player and Keynote are perhaps the two most obvious examples of this functionality.

The documentation for this is a little spotty so here’s the results of my investigation. My initial googling found this snippet from here.

UpdateSystemActivity(OverallAct);

The docs explain that you fire this every 30 seconds. On the surface, this looks like a hack and well, I don’t need a Carbon C call for that, I can fake a mouse event every 30 seconds.

This is out for good anyway, given that it doesn’t compile on Snow Leopard on x86_64. Carbon 64-bit will never arrive.

Turns out that the modern way to disable sleep uses IOKit. Apple has a doc for that too. Here is Listing 2:

#import 

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

IOPMAssertionID assertionID;
IOReturn success = IOPMAssertionCreate(kIOPMAssertionTypeNoDisplaySleep, 
                                    kIOPMAssertionLevelOn, &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. 
}

With any sample code that I’m gonna try to use in my apps, I take the time to understand what’s going on, and to read the docs for that with which I’m not familiar. (Don’t you? That’s fine, just throw someone else’s potential garbage in your app. I’m sure it won’t bite until you least expect it.)

I’m getting back an IOPMAssertionID, so I proceed to look that up in the XCode documentation viewer. And there’s nothing to be found. A look in IOPMLib.h tells me it’s a 32-bit unsigned integer and therefore not very interesting.

So, I move on to look up IOPMAssertionCreate() to find that it was introduced in Leopard only to be deprecated in Snow Leopard in favor of IOPMAssertionCreateWithName(). IOPMAssertionCreateWithName() has an extra parameter over IOPMAssertionCreate(), and it’s a name as you might imagine. I don’t care for having deprecated calls in my code so IOPMAssertionCreateWithName() it is. Caveat: IOPMAssertionCreateWithName() is not publicly available in Leopard, so either use IOPMAssertionCreate() in Xcode3.1/Leopard or compile in XCode 3.2/Snow Leopard and IOPMAssertionCreateWithName() will work when run on Leopard.

The sample code from Apple lists two assertion types that you can use: kIOPMAssertionTypeNoDisplaySleep that prevents display sleep, and kIOPMAssertionTypeNoIdleSleep that prevents idle sleep.

My testing indicates that kIOPMAssertionTypeNoDisplaySleep actually prevents both display and idle sleep. The intended use case for this is clearly along the lines of playing a movie or running a presentation.

kIOPMAssertionTypeNoIdleSleep will still allow the display to sleep, but the Mac never sleeps unless it runs out of battery power. Use cases for this fall into the category of ‘computations that may not finish before the computer sleeps’, such as bouncing an mixed audio/video file to disk or rendering a 3D image, etc.

Here is an example project that allows you to test both behaviors. Note: The project uses IOPMAssertionCreate() and not IOPMAssertionCreateWithName() in order to compile on both Leopard and Snow Leopard.

In keeping with good practice, I have filed bug reports with Apple for pieces of the documentation that I see as missing.

Comments

tomasf says:

Power Manager is 64-bit. They just messed up the headers for 64-bit. Declare UpdateSystemActivity yourself, and it works. We do this in Caffeine, and it works fine. No need for IOKit. :-)

Fraser Hess says:

@tomasf – If it’s not in the public headers, it’s private and I won’t touch it. If it should be in the public headers (which I doubt since it’s Carbon), a bug should be filed.